Re[9]: Можно ли избавиться от async|await?
От: SkyDance Земля  
Дата: 14.12.25 18:43
Оценка:
_>На C# для такой задачи ПМ только усложнять задачу. Вот полная консольная программа. Выведет: Finished in 100 ms.

Я привел это лишь для того, чтобы пояснить принцип решения первоначального вопроса ("избавиться от async/await"). Понятно же, что в разных clause'ах паттерн матчинга на самом деле будет записана какая-то более сложная логика, от scatter-gather до каких-нибудь еще решений. Мне лишь нужно было показать элегантность message passing по сравнению с "костылями" типа state machine этих самых async/await.
Re[8]: Можно ли избавиться от async|await?
От: mrTwister Россия  
Дата: 14.12.25 18:45
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>Что, конечно, не умаляет достоинств команды .NET и конкретно C#. Правильной дорогой идут, добавляя лучшее из других языков, и делая очень работоспособные высокоуровневые обертки. Именно эти обертки, от Task.Run до WhenAny, и есть ответ на вопрос из сабжа. Беда с ними лишь в том, что иногда это таки нужно дебажить...


Это еще не самая большая беда. Без легковесных потоков — это просто набор костылей, которые по сути мало что меняют.
лэт ми спик фром май харт
Re[9]: Можно ли избавиться от async|await?
От: SkyDance Земля  
Дата: 14.12.25 19:27
Оценка:
T>Это еще не самая большая беда. Без легковесных потоков — это просто набор костылей, которые по сути мало что меняют.

Так эти async/await и нужны именно для того, чтобы не писать рантайм по типу Эрланга.

Опять же, threadpool, если о нем думать как о scheduler'ах, и считать каждый Task отдельным "легковесным потоком", в общем-то, реализует данный сценарий. Просто инструментов для интроспекции там вообще нет, телеметрия ужасна, ну и в целом неэлегантно. Все-таки, Java уже скоро 30 (?) лет исполнится, а C# недалеко от нее ушел. В основном лишь в плане сахара.
Re[8]: Можно ли избавиться от async|await?
От: novitk США  
Дата: 14.12.25 20:01
Оценка:
Здравствуйте, mrTwister, Вы писали:

T>О да, есть Task.Run и ничего не надо переписывать. Ага, щаз! Потом продакшен внезапно перестает отвечать на любые запросы из-за thread pool depletion, так как скопилась очередь из Task.Run, которые выжрали все потоки из системного пула потоков и сервер встал колом

Ножом можно и убить, а можно помидор порезать. Разница по факту с твоим "go func(){result <- prime(10000)}()" только в дополнительной "церемонии" с каналами, а у SkyDance с процессами. Это не big deal в 90% задач, но абстракция async/аwait мошнее.
Отредактировано 14.12.2025 21:12 novitk . Предыдущая версия .
Re[10]: Можно ли избавиться от async|await?
От: novitk США  
Дата: 14.12.25 20:14
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>Мне лишь нужно было показать элегантность message passing по сравнению с "костылями" типа state machine этих самых async/await.

Это ты пока показом кодом не достиг, как мы видим, а мантрам "красиво, элегантно и очень легко дебажить" сложно возразить. Что до "молодцы в MS, содрали все хорошее с Erlang", то это просто чушь. Ни async/await(F#), ни PM(ML/Haskell) никакого отношения к Эрлангу не имеют.
Отредактировано 14.12.2025 20:16 novitk . Предыдущая версия .
Re[9]: Можно ли избавиться от async|await?
От: mrTwister Россия  
Дата: 14.12.25 21:13
Оценка: +1
Здравствуйте, novitk, Вы писали:

N>Ножом можно и убить, а можно помидор порезать.


Проблема в том, что через таски в C# убить можно случайно и незаметно в процессе порезания помидора. Например, можно сделать невинное свиду исправление, которое в какую-то синхронную функцию добавляет синхронный блокирующий вызов. Казалось бы, что такого: в синхронную функцию добавили синхронный выызов, что такого? Но потом оказывается, что это был первый блокирующий вызов в этой цепочке синхронных функций, которая инициируется из асинхронного контекста. И вуаля, асинхронный контекст заблокирован. И проверить это исправление на ревью или линтером очень сложно, потому что надо анализировать весь стек вызовов, а у нас все обмазано интерфейсами, абстрактными фабриками и поздним связыванием, и какой получится стек вызовов станет известно только в рантайме

N>Разница по факту с твоим "go func(){result <- prime(10000)}()" только в дополнительной "церемонии" возни с каналами, а у SkyDance с процессами. Это не big deal в 90% задач, но абстракция async/аwait мошнее.


Главная разница в том, что нет двух видов "цветов" функций. Нет никаких синхронных и асинхронных контекстов, которые надо постоянно учитывать.
лэт ми спик фром май харт
Re[10]: Можно ли избавиться от async|await?
От: mrTwister Россия  
Дата: 14.12.25 21:19
Оценка: +1
Здравствуйте, SkyDance, Вы писали:

SD>Так эти async/await и нужны именно для того, чтобы не писать рантайм по типу Эрланга.


Ну да, костыли в C# — вынужденная мера.

SD>Опять же, threadpool, если о нем думать как о scheduler'ах, и считать каждый Task отдельным "легковесным потоком", в общем-то, реализует данный сценарий. Просто инструментов для интроспекции там вообще нет, телеметрия ужасна, ну и в целом неэлегантно. Все-таки, Java уже скоро 30 (?) лет исполнится, а C# недалеко от нее ушел. В основном лишь в плане сахара.


Я бы согласился, если бы нельзя было создать блокирующую Task'у. Но в C# это делается очень легко и незаметно. После этого абстракция scheduler'а начинает течь со страшной силой, так как он уже не управляет потоками. Эти потоки у него могут забрать в любой момент и не отдать, после чего он уже schedul'ить не сможет
лэт ми спик фром май харт
Re[10]: Можно ли избавиться от async|await?
От: novitk США  
Дата: 14.12.25 21:32
Оценка:
Здравствуйте, mrTwister, Вы писали:

T>Проблема в том, что через таски в C# убить можно случайно и незаметно в процессе порезания помидора. Например, можно сделать невинное свиду исправление, которое в какую-то синхронную функцию добавляет синхронный блокирующий вызов. Казалось бы, что такого: в синхронную функцию добавили синхронный выызов, что такого? Но потом оказывается, что это был первый блокирующий вызов в этой цепочке синхронных функций, которая инициируется из асинхронного контекста. И вуаля, асинхронный контекст заблокирован. И проверить это исправление на ревью или линтером очень сложно, потому что надо анализировать весь стек вызовов, а у нас все обмазано интерфейсами, абстрактными фабриками и поздним связыванием, и какой получится стек вызовов станет известно только в рантайме

Согласен, бывает сплошь и рядом. Но ты так говоришь будто в GoLang это как-то решается. Оно решается?

T>Главная разница в том, что нет двух видов "цветов" функций. Нет никаких синхронных и асинхронных контекстов, которые надо постоянно учитывать.

Как же его нет, если ты или return пишешь или в каналы шлешь?
Re[7]: Можно ли избавиться от async|await?
От: Doom100500 Израиль  
Дата: 15.12.25 06:15
Оценка:
Здравствуйте, mrTwister, Вы писали:

T>Главное слово выделил, горутина — это внутренняя структура в go, которая не имеет никакого отношения к потокам. Это что-то типа класса Task в C#


D>>Программа на любом языке состоит из, как минимум, одного потока, который не блокирует современную многозагачную операционную сицтему.


T>Разница в том, что если, например в C# я запущу параллельно 1000 функций Thread.Sleep, то я создам и заблокирую 1000 потоков операционной системы, потому что Thread.Sleep синхронная. Если я запущу в go в горутинах 1000 функций time.Sleep, я не создам ни одного дополнительного потока операционной системы, потому что time.Sleep как и все остальные функции в go асинхронная. Аналогом time.Sleep из go является в .net Task.Delay, вот она асинхронная.


Как вызов функции foo() становится асинхронным? То, что потоки берутся из пула — это уже нюансы.
Task.Delay — и async/await — это про concurrency, который вовсе не обязательно parallelism. Вот и весь нюанс.
Простой вызов time.Sleep(1000), как и просто foo() в go блокирует текущую горутину, которая так, или иначе, выполняется на потоке (но тоток из заранее аллоцированного пула).

go time.Sleep(1000) — это асинхронно — создаём горутину и выполняем её на свободном потоке из пула, если нет, то ждём когда поток освободится. Это и есть отличие concurrency от parallelism.

Операционная система оперирует понятием "поток", а остальное — это навешанные абстракции. Чудес не бывает.

Concurrency is not parallelism
Спасибо за внимание
Re[11]: Можно ли избавиться от async|await?
От: mrTwister Россия  
Дата: 15.12.25 07:29
Оценка:
Здравствуйте, novitk, Вы писали:

N>Согласен, бывает сплошь и рядом. Но ты так говоришь будто в GoLang это как-то решается. Оно решается?


Да, в Go эта ситуация исключена, так как в Go все функции асинхронные и не могут блокировать потоки ОС. Даже если функция крутит бесконечный цикл, рантайм go ее в любой момент может остановить и передать поток OC другой горутине. Сам рантайм Go по умолчанию создает количество потоков ОС равное количеству ядер процессора, больше ему не надо.

T>>Главная разница в том, что нет двух видов "цветов" функций. Нет никаких синхронных и асинхронных контекстов, которые надо постоянно учитывать.

N>Как же его нет, если ты или return пишешь или в каналы шлешь?

Сама же функция при этом не меняется, ей без разницы как с результатом поступит вызывающая сторона: проигнорирует, положит в канал, или начнет сразу использовать.
лэт ми спик фром май харт
Отредактировано 15.12.2025 8:00 mrTwister . Предыдущая версия . Еще …
Отредактировано 15.12.2025 8:00 mrTwister . Предыдущая версия .
Re[8]: Можно ли избавиться от async|await?
От: mrTwister Россия  
Дата: 15.12.25 07:57
Оценка:
Здравствуйте, Doom100500, Вы писали:

D>Как вызов функции foo() становится асинхронным? То, что потоки берутся из пула — это уже нюансы.


Потому что он не блокирует поток ОС. При работе foo поток занимает, конечно, но foo не нужно дорабатывать до конца, чтобы отпустить этот поток.

D>Task.Delay — и async/await — это про concurrency, который вовсе не обязательно parallelism. Вот и весь нюанс.


Вот и любая функция в go (а так же горутины) — это тоже про concurrency, а не про параллелизм.

D>Простой вызов time.Sleep(1000), как и просто foo() в go блокирует текущую горутину, которая так, или иначе, выполняется на потоке (но тоток из заранее аллоцированного пула).


Горутину блокирует, а поток не блокирует. При вызове time.Sleep(1000) поток сразу отпускается и передается другой горутине. Тоже самое происходит при вызове Task.Delay в C#

D>go time.Sleep(1000) — это асинхронно — создаём горутину и выполняем её на свободном потоке из пула, если нет, то ждём когда поток освободится. Это и есть отличие concurrency от parallelism.


Горутина создается, но поток ОС она не потребляет, так как при вызове time.Sleep поток сразу отпускается.

D>Операционная система оперирует понятием "поток", а остальное — это навешанные абстракции. Чудес не бывает.

Ну да
лэт ми спик фром май харт
Отредактировано 15.12.2025 8:03 mrTwister . Предыдущая версия . Еще …
Отредактировано 15.12.2025 7:59 mrTwister . Предыдущая версия .
Re[9]: Можно ли избавиться от async|await?
От: Doom100500 Израиль  
Дата: 15.12.25 08:09
Оценка:
Здравствуйте, mrTwister, Вы писали:

T>При вызове time.Sleep(1000) поток сразу отпускается и передается другой горутине. Тоже самое происходит при вызове Task.Delay в C#


Ок, дошло. Но почему вызов foo(), в которой обычные императивные вычисления — асинхронный? При условии, что там не используются ни sync, ни Sleep, ни каналы, ни select. Такой вызов и есть "по умолчанию".
Спасибо за внимание
Re[10]: Можно ли избавиться от async|await?
От: mrTwister Россия  
Дата: 15.12.25 08:15
Оценка: 3 (2)
Здравствуйте, Doom100500, Вы писали:

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


T>>При вызове time.Sleep(1000) поток сразу отпускается и передается другой горутине. Тоже самое происходит при вызове Task.Delay в C#


D>Ок, дошло. Но почему вызов foo(), в которой обычные императивные вычисления — асинхронный? При условии, что там не используются ни sync, ни Sleep, ни каналы, ни select. Такой вызов и есть "по умолчанию".


Потому что компилятор принудительно в foo расставит точки останова, в которых будет отпускаться поток ОС. И даже если в foo крутится пустой бесконечный цикл, рантайм go через особые хаки такую точку останова туда внедрит.
лэт ми спик фром май харт
Re[6]: Можно ли избавиться от async|await?
От: Артём Австралия жж
Дата: 15.12.25 09:13
Оценка:
Здравствуйте, SkyDance, Вы писали:

Аё>>Написать go вместо async и receive вместо await делает код легкочитабельным?


SD>await разве умеет pattern matching?

Какая связь?
Вообще мне го нравился его протоколами. Но дальше утилки для конечного пользователя у нас на продукте дело не пошло, к сожалению. Нет популярного web UI под го (и переписывать с ts нет смысла), а бек- что не заняла жава, дополнил питон и нод ts.
Re[11]: Можно ли избавиться от async|await?
От: Doom100500 Израиль  
Дата: 15.12.25 09:24
Оценка:
Здравствуйте, mrTwister, Вы писали:

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


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


T>>>При вызове time.Sleep(1000) поток сразу отпускается и передается другой горутине. Тоже самое происходит при вызове Task.Delay в C#


D>>Ок, дошло. Но почему вызов foo(), в которой обычные императивные вычисления — асинхронный? При условии, что там не используются ни sync, ни Sleep, ни каналы, ни select. Такой вызов и есть "по умолчанию".


T>Потому что компилятор принудительно в foo расставит точки останова, в которых будет отпускаться поток ОС. И даже если в foo крутится пустой бесконечный цикл, рантайм go через особые хаки такую точку останова туда внедрит.


Ну так это не про вызовы, а вообще про все инструкции. Функция может и заинлайниться.
В любом случае, понятно почему всё async.
Спасибо за внимание
Отредактировано 15.12.2025 9:26 Doom100500 . Предыдущая версия . Еще …
Отредактировано 15.12.2025 9:25 Doom100500 . Предыдущая версия .
Re[12]: Можно ли избавиться от async|await?
От: mrTwister Россия  
Дата: 15.12.25 09:26
Оценка:
Здравствуйте, Doom100500, Вы писали:

D>Ну так это не про вызовы, а вообще про все инструкции. Функция может и заинлайниться.


Не важно, компилятор с рантаймом это все контролируют и обрабатывают.
лэт ми спик фром май харт
Re[10]: Можно ли избавиться от async|await?
От: · Великобритания  
Дата: 15.12.25 09:56
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>Опять же, threadpool, если о нем думать как о scheduler'ах, и считать каждый Task отдельным "легковесным потоком", в общем-то, реализует данный сценарий. Просто инструментов для интроспекции там вообще нет, телеметрия ужасна, ну и в целом неэлегантно. Все-таки, Java уже скоро 30 (?) лет исполнится, а C# недалеко от нее ушел. В основном лишь в плане сахара.

В java уже virtual threads есть. По-моему, самое удобное.
Просто пишешь многопоточный код, как обычно. И запускаешь сколько угодно тредов.
Ещё structured concurrency допиливают.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[11]: Можно ли избавиться от async|await?
От: · Великобритания  
Дата: 15.12.25 10:09
Оценка: +1
Здравствуйте, mrTwister, Вы писали:

D>>Ок, дошло. Но почему вызов foo(), в которой обычные императивные вычисления — асинхронный? При условии, что там не используются ни sync, ни Sleep, ни каналы, ни select. Такой вызов и есть "по умолчанию".

T>Потому что компилятор принудительно в foo расставит точки останова, в которых будет отпускаться поток ОС. И даже если в foo крутится пустой бесконечный цикл, рантайм go через особые хаки такую точку останова туда внедрит.
Не очень понял в чём разница. Это называется вытесняющая многозадачность. ОС сама отпускает потоки, даже если там бесконечные циклы крутятся.

Основная беда в C# что если ты даже обернул некий GetPrime в async, то это мало что даёт. Например, где-то в глубине этого GetPrime может быть какой-то код, который делает синхронные вызовы с блокировкой. Ну, например, ходит в сетевой кеш. И в итоге они блокируют ОС-поток, несмотря на твой async. И в этом случае придётся перекрашивать функции на всю глубину вызова, переписывать дохрена кода. Особенно весело, когда это библиотечный код, к которому нет доступа.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Можно ли избавиться от async|await?
От: Философ Ад http://vk.com/id10256428
Дата: 15.12.25 11:38
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Т.к. в основном асинхронных больше и даже если какая не асинхронная — то компилятор мог бы сам оптимизировать.


Это в какой вселенной? Если правда, то зачем это намудили?
Покажи код, где асинхронных больше.
Всё сказанное выше — личное мнение, если не указано обратное.
Re[12]: Можно ли избавиться от async|await?
От: mrTwister Россия  
Дата: 15.12.25 12:03
Оценка:
Здравствуйте, ·, Вы писали:

T>>Потому что компилятор принудительно в foo расставит точки останова, в которых будет отпускаться поток ОС. И даже если в foo крутится пустой бесконечный цикл, рантайм go через особые хаки такую точку останова туда внедрит.

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

async/await — это и есть по сути попытка реализации кооперативной многозадачности в рамках ограниченного количества потоков ОС. В go эта многозадачность реализована более грамотно и она там действительно не кооперативная, а вытесняющая

·>Основная беда в C# что если ты даже обернул некий GetPrime в async, то это мало что даёт. Например, где-то в глубине этого GetPrime может быть какой-то код, который делает синхронные вызовы с блокировкой. Ну, например, ходит в сетевой кеш. И в итоге они блокируют ОС-поток, несмотря на твой async. И в этом случае придётся перекрашивать функции на всю глубину вызова, переписывать дохрена кода. Особенно весело, когда это библиотечный код, к которому нет доступа.


Об этом и речь
лэт ми спик фром май харт
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.