Re[10]: Горутины и потоки
От: mrTwister Россия  
Дата: 28.06.21 14:43
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

G>То есть стеки всех потоков хранятся в одном адресном пространстве?


В рамках одного процесса — да, стеки всех потоков этого процесса живут в одном адресном пространстве. Переключение контекста процесса — это уже другая история, там виртуальная память переключается (вместе со всеми данными, включая стеки).
лэт ми спик фром май харт
Re[5]: Горутины и потоки
От: Cyberax Марс  
Дата: 28.06.21 17:11
Оценка:
Здравствуйте, netch80, Вы писали:

N>В Go это точно так же как в случае ОС — переход в ожидание, или просто вызов чего-то в рантайме. Пустой вечный цикл в Go заблокирует целиком одну системную нить рантайма.

Уже нет. В Go 1.14 добавили асинхронные прерывания: https://medium.com/a-journey-with-go/go-asynchronous-preemption-b5194227371c
Sapienti sat!
Re[7]: Горутины и потоки
От: Cyberax Марс  
Дата: 28.06.21 17:12
Оценка:
Здравствуйте, gandjustas, Вы писали:

T>>Не заблокирует, начиная с версии 1.14 в Go реализована вытесняющая многозадачность, рантайм переключает горутины в произвольные моменты времени независимо от того, что они делают

G>А зачем? ОС справляется хуже? Или сам по коду точки прерывания расставляет?
Для отзывчивости GC. В первой фазе ему нужно на короткое время остановить все потоки на safepoint'ах.
Sapienti sat!
Re[7]: Горутины и потоки
От: Sharov Россия  
Дата: 28.06.21 17:20
Оценка:
Здравствуйте, gandjustas, Вы писали:

T>>Не заблокирует, начиная с версии 1.14 в Go реализована вытесняющая многозадачность, рантайм переключает горутины в произвольные моменты времени независимо от того, что они делают

G>А зачем? ОС справляется хуже? Или сам по коду точки прерывания расставляет?

А зачем ОС для этого вообще?
Кодом людям нужно помогать!
Re[5]: Горутины и потоки
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 28.06.21 18:40
Оценка: 6 (1)
Здравствуйте, mrTwister, Вы писали:

T>Ок, спасибо, стало яснее. Но тогда разработчики ОС могли бы, например, предоставить возможность запускать легковесные потоки отдельно от старых тяжеловесных, введя ограничение на безопасность таких потоков и необходимость предоставлять информацию по разметке стека.


В какой-то мере это было, гуглить про "M:N threading". В Solaris такое делали, во FreeBSD пытались. Основные попытки шли в период 1995-2005 и оказалось, что это хуже работает, чем прямой 1:1, потому что сложность такого двухуровневого шедулинга забивает всё.

Сейчас, возможно, наступило время следующих попыток, на более высоком уровне.
The God is real, unless declared integer.
Re[6]: Горутины и потоки
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 28.06.21 18:46
Оценка:
Здравствуйте, Serginio1, Вы писали:

S> Ну там написано и про кэши при межпроцессном переключениию

S>https://ru.wikipedia.org/wiki/%D0%9F%D0%B5%D1%80%D0%B5%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82%D0%B0
S>

S>Содержимое кэша (особенно это касается кэша первого уровня), накопленное и «оптимизированное» под выполнение одного потока, оказывается совершенно неприменимым к новому потоку, на который происходит переключение.
S>При переключении контекста на процесс, который до этого долгое время не использовался (см. Подкачка страниц), многие страницы могут физически отсутствовать в оперативной памяти, что порождает подгрузку вытесненных страниц из вторичной памяти.


Да.

S>[q]

S>С точки зрения прикладного уровня переключение контекста можно разделить на добровольное (voluntary) и принудительное (non-voluntary): выполняющийся процесс/поток может сам передать управление другому потоку либо ядро может насильно отобрать у него управление.

S>Ядро ОС может отобрать управление у выполняющегося процесса/потока при истечении кванта времени, выделенного на выполнение. С точки зрения программиста это означает, что управление могло уйти от потока в «самый неподходящий» момент времени, когда структуры данных могут находиться в противоречивом состоянии из-за того, что их изменение не было завершено.


И что? ОС всё равно должна будет сделать это рано или поздно, если задача сама не отдаёт управление. А если отдаёт, то зачем ОС принудительно переключать?

S> Поэтому async await то есть задачи предпочтительнее потоков


Нет, само по себе ничего из цитированного не является тут аргументом в сторону async/await. Там точно так же — если управление отдано явно, то шедулер не будет забирать насильно, а если нет, то заберёт — скорее всего, средствами ОС. Более того, если в момент переключения на входе или выходе await не будет явно сказано "а теперь подумайте, не переключить ли" соответствующим системным вызовом, то ОС не будет знать, когда там userland занимается переключением вокруг awaitʼа, и тоже переключит в непредсказуемый (и, возможно, неподходящий) момент.
The God is real, unless declared integer.
Re[10]: Горутины и потоки
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 28.06.21 18:50
Оценка: 6 (1)
Здравствуйте, gandjustas, Вы писали:

G>>>Работает поток: вызвал функцию А, она функцию Б, она функцию В. Произошло прерывание. Через пару десятков мсек возвращает правление потоку 1, возвращает стек на место, возвращает регистры и thread local значения. И функция В как ни в чем не бывало продолжает выполняться, возвращает управление функции Б, а та в свою очрередь функции А.


T>>Стек сам вернется после возвращения стековых регистров, память процесса же не стирается


G>То есть стеки всех потоков хранятся в одном адресном пространстве?


Естественно, в одном. Все нитки видят все стеки и могут обращаться в пределах разрешённого системой защиты.
А иначе бы не работало, например, когда в main() создаётся объект глобальной обстановки и он дальше по ссылке/указателю передаётся остальным, или когда рабочие нитки пула обращаются к состоянию управляющей нитки.

И даже thread-local память сидит в общем пространстве — меняется только приписанный к конкретной нитке адрес начала персональной области (на x86 обычно в регистре FS и/или GS).
The God is real, unless declared integer.
Re[6]: Горутины и потоки
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 28.06.21 18:58
Оценка: 6 (1)
Здравствуйте, gandjustas, Вы писали:

G>>>ОС не знает когда поток остановится и остановится ли вообще.

N>>Ну кроме случаев, когда нить сама делает вызов, который блокирует её выполнение )
N>>а таких действий в сетевой нагрузке и GUI, например, большинство.
G>А конкретнее?

Эээ... что именно конкретнее?

G>Если что планировщики ОС всгеда нормально справлялись с десктопной нагрузкой. необходимость обращатся с огромным количеством потоков есть только в сервреах.


А что из этого влияет на сказанное мной?
И IO bound, и CPU bound нагрузка может быть и на сервере, и на десктопе.

G>>> ОС прерывает выполнение по кванту времени, поэтому нужно сохвранить все текуще состояние (стек).

N>>При любом прерывании выполнения надо сохранить текущее состояние.
G>Только "контролируемое" прерывание потребует гораздо меньше места для сохраннеия, чем прерывание в произвольном месте.

Ну да, потому что необходимое для этого сохранение предварительно сделано (а потом будет восстановление) самим прерываемым кодом. Сохранения в идеальном варианте столько же, но отличается место и исполнитель. Если же кто-то видит суммарный объём действий по такому сохранению меньше, чем в варианте разрыва в произвольном месте, то там неэффективно используют ресурсы процессора — в первую очередь заполняют не все регистры.

(В частности, этим как раз отличается Go — там из-за NIH написания кодогенератора нет передачи аргументов и результатов функции в регистрах, всё на стеке, и даже внутри функций основная часть кода это прочитать из одного места стека и записать тут же в другое, а потом обратно.)

Там, где ресурсы используются эффективно, неизбежно при переключениях сохраняются и восстанавливаются все неслужебные регистры, и неважно, переключение произошло добровольно или через таймерный сигнал.

G>>> В "упавлемых" средах "поток" выполнения останавливается не тогда когда ОС решит, а тогда когда код дойдет до точки прерывания.

N>>И как определяется точка прерывания?
N>>В Go это точно так же как в случае ОС — переход в ожидание, или просто вызов чего-то в рантайме. Пустой вечный цикл в Go заблокирует целиком одну системную нить рантайма.
G>Это фактически означает что планирощик Го не может использоваться в ОС для всех потоков.

Про то, что недавно это таки исправили, уже написали.
Но в общем да, внутренний планировщик Go не может использоваться в ядре, или о чём речь?

N>>Оно ровно равно тут всему стеку плюс регистры (а не пространству, зарезервированному под стек).

G>В C# можно в явном виде увидеть состояние "продожения" в async\await, оно сильно меньше чем весь стек + регистры.

В таком случае сохранение и восстановление сделано внутренними средствами рантайма как раз на границе точки разрезания. Как это конкретно сделано — значения скинуты в стек, в состояние объекта или ещё куда-то — это уже немного менее важно.
The God is real, unless declared integer.
Отредактировано 29.06.2021 6:21 netch80 . Предыдущая версия .
Re[5]: Горутины и потоки
От: Sharov Россия  
Дата: 29.06.21 00:04
Оценка:
Здравствуйте, netch80, Вы писали:


N>В Go это точно так же как в случае ОС — переход в ожидание, или просто вызов чего-то в рантайме. Пустой вечный цикл в Go заблокирует целиком одну системную нить рантайма.


А чем системная нить rt в Го отличается от потока ОС?


N>В Erlang не так — там рантайм считает "редукции", но за это платится тратой процессора.


А это что такое, в двух словах, если можно?
Кодом людям нужно помогать!
Re[7]: Горутины и потоки
От: Sharov Россия  
Дата: 29.06.21 00:39
Оценка:
Здравствуйте, netch80, Вы писали:

N>Про то, что недавно это таки исправили, уже написали.

N>Но в общем да, внутренний планировщик Go не может использоваться в ядре, или о чём речь?

Ну так это и есть суть вопроса ТС -- потоки переключает соотв. планировщик (го, .net и т.п.), а уже ОС только процессы.
В таком случае зеленые потоки будут просто не нужны. Ну т.е. в ядре ОС выполнять код соотв. стороннего планировщика.
Кодом людям нужно помогать!
Re[8]: Горутины и потоки
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 29.06.21 05:56
Оценка: 93 (3)
Здравствуйте, Sharov, Вы писали:

S>Здравствуйте, netch80, Вы писали:


N>>Про то, что недавно это таки исправили, уже написали.

N>>Но в общем да, внутренний планировщик Go не может использоваться в ядре, или о чём речь?

S>Ну так это и есть суть вопроса ТС


Как по мне, вопрос заметно в другом.

S> -- потоки переключает соотв. планировщик (го, .net и т.п.), а уже ОС только процессы.


В таком виде на современном железе никому не будет нужно.
Больше одного харта (hardware thread) => можно исполнять несколько задач (любого вида) одновременно, значит, надо уметь исполнять и несколько нитей (тредов), которые с точки зрения ОС относятся к одному процессу. Что в процессе это будут именно диспетчерские нити, которые будут выполнять по своему желанию конкретные нити целевого кода, это уже не вопрос ОС.

S>В таком случае зеленые потоки будут просто не нужны. Ну т.е. в ядре ОС выполнять код соотв. стороннего планировщика.


И кто пустит посторонний код в ядро?

Похожие разработки как раз были. Во FreeBSD игрались с подходом "scheduler activations". Это выглядело так: если нить блокируется в ядре, ядро запускает указанный процессом пользовательский код с указанием "а подумай, не переключиться ли на что-то ещё". Пользовательский код управляет тем, какие из видимых ниток должны исполняться.
Разработка шла приблизительно в 1999-2002. После отказались и сделали тупо 1:1, все нити процесса известны ядру и оно их переключает. Вот не справились со сложностью.
Насколько мне известно, после этого таких попыток не было.
Go, Erlang, прочие — там внутренний шедулер процесса имеет сильно больше возможностей знать контекст и управлять им, и поэтому ему легче.
The God is real, unless declared integer.
Re[6]: Горутины и потоки
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 29.06.21 05:59
Оценка: 6 (1)
Здравствуйте, Sharov, Вы писали:

N>>В Go это точно так же как в случае ОС — переход в ожидание, или просто вызов чего-то в рантайме. Пустой вечный цикл в Go заблокирует целиком одну системную нить рантайма.

S>А чем системная нить rt в Го отличается от потока ОС?

Это одно и то же. (И, на всякий случай, нить = thread в нормальной терминологии, а поток это stream или flow, но не thread.)
Рантайм Go держит максимум указанное в GOMAXPROCS (да, название сомнительно) количество рабочих нитей (умолчание равно количеству harts == hardware threads, которое равно количеству ядер без гипертрединга и умножается на количество тредов в ядре для включённого гипертрединга; в документации Go, однако, эти сущности называются CPU). Внутренний планировщик решает, какую горутину будет исполнять конкретная нить.

N>>В Erlang не так — там рантайм считает "редукции", но за это платится тратой процессора.

S>А это что такое, в двух словах, если можно?

Выполнение одного элементарного действия (вызов функции, выполнение арифметической операции...)
The God is real, unless declared integer.
Отредактировано 29.06.2021 6:11 netch80 . Предыдущая версия .
Re[7]: Горутины и потоки
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 29.06.21 15:11
Оценка:
Здравствуйте, netch80, Вы писали:


S>>Ядро ОС может отобрать управление у выполняющегося процесса/потока при истечении кванта времени, выделенного на выполнение. С точки зрения программиста это означает, что управление могло уйти от потока в «самый неподходящий» момент времени, когда структуры данных могут находиться в противоречивом состоянии из-за того, что их изменение не было завершено.


N>И что? ОС всё равно должна будет сделать это рано или поздно, если задача сама не отдаёт управление. А если отдаёт, то зачем ОС принудительно переключать?

То что желательно указать в какие моменты можно переключать. Например дождаться выполнения lock
S>> Поэтому async await то есть задачи предпочтительнее потоков

N>Нет, само по себе ничего из цитированного не является тут аргументом в сторону async/await. Там точно так же — если управление отдано явно, то шедулер не будет забирать насильно, а если нет, то заберёт — скорее всего, средствами ОС. Более того, если в момент переключения на входе или выходе await не будет явно сказано "а теперь подумайте, не переключить ли" соответствующим системным вызовом, то ОС не будет знать, когда там userland занимается переключением вокруг awaitʼа, и тоже переключит в непредсказуемый (и, возможно, неподходящий) момент.

Ну как же не. Как раз проблема в большей степени это проблемы с Синхронизирующие примитивы ядра. Мьютексы, Семафоры, эвенты и т. д.

Для примера в эпоху до async/await поток ждет выполнения асинхронной операции.
async/await берет на себя сохранения данных внутри класса (стек не нужен) и строит автомат и тот же поток который выполнял данную задачу, запускает другую. Нет никаких переключений.
Не зря же используют SpinLock. Если бы стоимость переключения была не важна, то нафига он нужен?
Зачем тогда async/await

Ну и замена всяких Lock на ManualResetValueTaskSource
http://rsdn.org/forum/dotnet/8030645.1
Автор: Serginio1
Дата: 16.06.21

https://stackoverflow.com/questions/66387225/awaiting-a-single-net-event-with-a-valuetask

Конечно зависит от длительности задач, но если задачи непродолжительные, то пул потоков может и не переключаться а выполнять очередь заданий.
и солнце б утром не вставало, когда бы не было меня
Re[2]: Горутины и потоки
От: vdimas Россия  
Дата: 29.06.21 23:42
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>1) Переключение контекста не бесплатное


Для потоков одного процесса должно быть бесплатным.

G>2) Каждый поток кушает 1МБ под стек минимум


Настраивается.
И эта же проблема присутствует в любой реализации "зеленых потоков".
Re[3]: Горутины и потоки
От: mrTwister Россия  
Дата: 30.06.21 07:08
Оценка:
Здравствуйте, vdimas, Вы писали:

V>И эта же проблема присутствует в любой реализации "зеленых потоков".


В горутинах этой проблемы нет, там размер стека динамический: начинает с 2Kb, и может увеличиваться по мере надобности до 1Gb
лэт ми спик фром май харт
Re[4]: Горутины и потоки
От: vdimas Россия  
Дата: 30.06.21 10:13
Оценка:
Здравствуйте, mrTwister, Вы писали:

V>>И эта же проблема присутствует в любой реализации "зеленых потоков".

T>В горутинах этой проблемы нет, там размер стека динамический: начинает с 2Kb, и может увеличиваться по мере надобности до 1Gb

Это можно сделать и в С++ с некоторыми ограничениями, а именно — не вываливать наружу адреса стековых объектов и чтобы сами объекты, создаваемые по-значению на стеке, не оперировали адресами своего расположения. В управляемых языках, поддерживающих перемещение объектов в памяти как часть алгоритма GC, ограничений меньше, ес-но.

Прологи-эпилоги для каждой архитектуры известны, переполнение стека перехватывается, можно выделять новые фреймы стека по мере надобности, перенося туда стековую память текущей ф-ии, которой не хватило стека.
Re[11]: Горутины и потоки
От: Sharov Россия  
Дата: 30.06.21 10:51
Оценка:
Здравствуйте, netch80, Вы писали:

N>И даже thread-local память сидит в общем пространстве — меняется только приписанный к конкретной нитке адрес начала персональной области (на x86 обычно в регистре FS и/или GS).


А как локальность страницы в FS\GS решается? На уровне параметров (дескриптора) самой страницы? Ну т.е. почему DS общий для всего
процесса (всех потоков), а fs только для одного?
Кодом людям нужно помогать!
Re[12]: Горутины и потоки
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 30.06.21 16:01
Оценка: 6 (1)
Здравствуйте, Sharov, Вы писали:

N>>И даже thread-local память сидит в общем пространстве — меняется только приписанный к конкретной нитке адрес начала персональной области (на x86 обычно в регистре FS и/или GS).


S>А как локальность страницы в FS\GS решается? На уровне параметров (дескриптора) самой страницы?


Локальность в смысле доступа только одной нити — никак не решается — каждая может что-то сделать с данными каждой другой нити, если знает, как (знает адрес).
Все TLS области мапятся на одно общее адресное пространство процесса.

S> Ну т.е. почему DS общий для всего

S>процесса (всех потоков), а fs только для одного?

FS и GS в этом режиме это уже не сегментные дескрипторы, а просто регистры — указатели (причём в x86-32 это опционально, а в x86-64 уже форсировано).
Просто служебный регистр, который можно использовать только как базовый адрес чего-то. Вот он и используется как адрес thread-local области.

Ну и DS в обычном сетапе в x86-32, и единственно возможно в x86-64, это просто "вся виртуальная память с адреса 0".
The God is real, unless declared integer.
Re[8]: Горутины и потоки
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 30.06.21 18:23
Оценка:
Здравствуйте, Serginio1, Вы писали:

N>>И что? ОС всё равно должна будет сделать это рано или поздно, если задача сама не отдаёт управление. А если отдаёт, то зачем ОС принудительно переключать?

S>То что желательно указать в какие моменты можно переключать. Например дождаться выполнения lock

Эээ... после выполнения захвата лока (речь об этом, да?) как раз лучше не отдавать управление, а сделать максимум действий во время захвата. А до него — если лок занят — то раз ждём, надо оповестить шедулер о том, что есть отличный повод запустить другую задачу.

N>>Нет, само по себе ничего из цитированного не является тут аргументом в сторону async/await. Там точно так же — если управление отдано явно, то шедулер не будет забирать насильно, а если нет, то заберёт — скорее всего, средствами ОС. Более того, если в момент переключения на входе или выходе await не будет явно сказано "а теперь подумайте, не переключить ли" соответствующим системным вызовом, то ОС не будет знать, когда там userland занимается переключением вокруг awaitʼа, и тоже переключит в непредсказуемый (и, возможно, неподходящий) момент.

S> Ну как же не. Как раз проблема в большей степени это проблемы с Синхронизирующие примитивы ядра. Мьютексы, Семафоры, эвенты и т. д.

И в чём тут проблема?

S>Для примера в эпоху до async/await поток ждет выполнения асинхронной операции.

S>async/await берет на себя сохранения данных внутри класса (стек не нужен) и строит автомат и тот же поток который выполнял данную задачу, запускает другую. Нет никаких переключений.

А что, вот эти все "берёт на себя сохранения данных внутри класса" и аналогичное восстановление, по-вашему, не переключение? А что оно такое тогда?

S> Не зря же используют SpinLock. Если бы стоимость переключения была не важна, то нафига он нужен?


Вот этот, да?

Действительно, стоимость переключения (любого — в том числе которого вы не замечаете цитатой выше) высока. Но при чём тут спинлоки?
Спинлок это очень специфичное средство, которое становится эффективным только при одновременном выполнении двух условий: 1) конкуренты за лок чаще находятся на разных физических исполнителях (железных тредах, harts) и 2) длительность возможной конкуренции относительно мала. Иначе трата процессора на кручение вокруг ячейки памяти в ожидании, пока в той появится заветный нолик, слишком велика — лучше таки переключиться.

Классический мьютекс ещё со времён Solaris поэтому делает N попыток захвата как спинлока (процессорной операцией типа CAS, LL/SC), а если не получилось — уходит в ядерный вызов. В Linux сделано (в futex) так же. Что в Windows, не знаю, облом докапываться до исходников. Но использовать SpinLock как аргумент в пользу дороговизны переключения — это, мягко говоря, странно.

S>Зачем тогда async/await


С точки зрения логики управления выполнением — именно для тех случаев, когда шедулер узнал, что ответа для await сейчас нет и надо дать кому-то управление.
С точки зрения исходного кода — чтобы писать его максимально линейно.
Переключений в нём, в идеале, столько же, сколько в аналогичном коде на коллбэках или промисах.

S>Ну и замена всяких Lock на ManualResetValueTaskSource

S>http://rsdn.org/forum/dotnet/8030645.1
Автор: Serginio1
Дата: 16.06.21

S> https://stackoverflow.com/questions/66387225/awaiting-a-single-net-event-with-a-valuetask

Я что-то не могу это раскурить. Какой смысл в его применении?

S>Конечно зависит от длительности задач, но если задачи непродолжительные, то пул потоков может и не переключаться а выполнять очередь заданий.


Тоже не понимаю, при чём тут эта реплика.
The God is real, unless declared integer.
Re[9]: Горутины и потоки
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 01.07.21 07:58
Оценка:
Здравствуйте, netch80, Вы писали:

N>Здравствуйте, Serginio1, Вы писали:


N>>>И что? ОС всё равно должна будет сделать это рано или поздно, если задача сама не отдаёт управление. А если отдаёт, то зачем ОС принудительно переключать?

S>>То что желательно указать в какие моменты можно переключать. Например дождаться выполнения lock

N>Эээ... после выполнения захвата лока (речь об этом, да?) как раз лучше не отдавать управление, а сделать максимум действий во время захвата. А до него — если лок занят — то раз ждём, надо оповестить шедулер о том, что есть отличный повод запустить другую задачу.

Я как раз про то, что если lock короткий, то не стоит передавать управление на другой поток.

N>>>Нет, само по себе ничего из цитированного не является тут аргументом в сторону async/await. Там точно так же — если управление отдано явно, то шедулер не будет забирать насильно, а если нет, то заберёт — скорее всего, средствами ОС. Более того, если в момент переключения на входе или выходе await не будет явно сказано "а теперь подумайте, не переключить ли" соответствующим системным вызовом, то ОС не будет знать, когда там userland занимается переключением вокруг awaitʼа, и тоже переключит в непредсказуемый (и, возможно, неподходящий) момент.

S>> Ну как же не. Как раз проблема в большей степени это проблемы с Синхронизирующие примитивы ядра. Мьютексы, Семафоры, эвенты и т. д.

N>И в чём тут проблема?


https://ru.wikipedia.org/wiki/%D0%9F%D0%B5%D1%80%D0%B5%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82%D0%B0

Синхронизирующие примитивы ядра. Мьютексы, Семафоры и т. д. Это и есть основной источник проблем с производительностью. Недостаточно продуманная работа с синхронизирующими примитивами может приводить к десяткам тысяч, а в особо запущенных случаях — и к сотням тысяч переключений контекста в секунду. [источник не указан 2477 дней]

S>>Для примера в эпоху до async/await поток ждет выполнения асинхронной операции.
S>>async/await берет на себя сохранения данных внутри класса (стек не нужен) и строит автомат и тот же поток который выполнял данную задачу, запускает другую. Нет никаких переключений.

N>А что, вот эти все "берёт на себя сохранения данных внутри класса" и аналогичное восстановление, по-вашему, не переключение? А что оно такое тогда?

Там нет восстаеовления ибо данные хранятся в куче. Да должны закинуть this в регистр и вызвать MoveNext
S>> Не зря же используют SpinLock. Если бы стоимость переключения была не важна, то нафига он нужен?

N>Вот этот, да?


N>Действительно, стоимость переключения (любого — в том числе которого вы не замечаете цитатой выше) высока. Но при чём тут спинлоки?

N>Спинлок это очень специфичное средство, которое становится эффективным только при одновременном выполнении двух условий: 1) конкуренты за лок чаще находятся на разных физических исполнителях (железных тредах, harts) и 2) длительность возможной конкуренции относительно мала. Иначе трата процессора на кручение вокруг ячейки памяти в ожидании, пока в той появится заветный нолик, слишком велика — лучше таки переключиться.

N>Классический мьютекс ещё со времён Solaris поэтому делает N попыток захвата как спинлока (процессорной операцией типа CAS, LL/SC), а если не получилось — уходит в ядерный вызов. В Linux сделано (в futex) так же. Что в Windows, не знаю, облом докапываться до исходников. Но использовать SpinLock как аргумент в пользу дороговизны переключения — это, мягко говоря, странно.


S>>Зачем тогда async/await


N>С точки зрения логики управления выполнением — именно для тех случаев, когда шедулер узнал, что ответа для await сейчас нет и надо дать кому-то управление.

N>С точки зрения исходного кода — чтобы писать его максимально линейно.
Линейно то и раньше писали, только внутри были всякие евенты и остановка потока.
N>Переключений в нём, в идеале, столько же, сколько в аналогичном коде на коллбэках или промисах.
Ну в итоге то колбеки запускаются из пула потоков.

S>>Ну и замена всяких Lock на ManualResetValueTaskSource

S>>http://rsdn.org/forum/dotnet/8030645.1
Автор: Serginio1
Дата: 16.06.21

S>> https://stackoverflow.com/questions/66387225/awaiting-a-single-net-event-with-a-valuetask

N>Я что-то не могу это раскурить. Какой смысл в его применении?

Смысл в том, что замена lock на lockAsync. То есть нет никакого ожидания потока
S>>Конечно зависит от длительности задач, но если задачи непродолжительные, то пул потоков может и не переключаться а выполнять очередь заданий.

N>Тоже не понимаю, при чём тут эта реплика.

В том, что с использованием пула потоков сводит к минимуму переключение потоков
и солнце б утром не вставало, когда бы не было меня
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.