Здравствуйте, alex_public, Вы писали:
_>2. И read и write можно вызывать в любых потоках. Тогда вообще всё просто и не нужны все эти хитрости типа await/async. Вот корректный асинхронный код: _>
_>thread([]{
_> auto value=read();
_> write(next(value));
_>}).detach();
_>
Потрясающе. Если тебе понадобится обрабатывать сотни подключений у тебя будут сотни потоков. Практический лимит потоков на windows — около 200. Но даже при сотне уже все тормозит. При этом потоки будут ничего не делать.
Кстати код который создает потоки для выполнения называют параллельным, а не асинхронным. Асинхронный это как раз когда потоки без нужды не создаются.
_>А если нам требуется максимальное быстродействие и read и write относятся к разным устройствам, то можно вообще завести 2 независимых потока со своими буферами и синхронизацией (например через обмен сообщений по модели акторов). Но здесь уже понятно что не обойтись без модификации кода.
Ну как-бы проблему множества подключений не решает.
_>В C# всё тоже самое, за исключением того, что обязательно требуется менять указанный код (добавить await). Причём менять надо не только его, но и весь стек вызова (добавить async).
Не тоже самое. Ты просто нихрена в этом не понимаешь
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, gandjustas, Вы писали:
G>>Компилируемый код дай.
_>Это он и есть. ) Точнее его часть, отвечающая за обсуждаемый вопрос. Остальной код касается исключительно вывода на экран окошка с кнопкой, причём реализованного через жирную GUI библиотеку, так что ты его всё равно у себя не соберёшь. Или же не поленишься поставить себе эту GUI библиотеку ради форумного спора? )))
Ты все зависимости в исходники включи и залей архив сюда. Желательно чтобы в VS собиралось.
Здравствуйте, gandjustas, Вы писали:
G>Потрясающе. Если тебе понадобится обрабатывать сотни подключений у тебя будут сотни потоков. Практический лимит потоков на windows — около 200. Но даже при сотне уже все тормозит. При этом потоки будут ничего не делать.
Если нам потребуются легковесные потоки, то Boost.Coroutine прямо их и реализует. Т.е. уже без моих хитрых макросов для реализации await/async. Причём делает это несравнимо эффективнее других, обсуждаемых тут, реализаций.
Однако в разговоре с Ikemefula, в который ты вклинился, речь шла совсем не о легковесных потоках. Так что непонятно к чему ты это всё написал.
G>Кстати код который создает потоки для выполнения называют параллельным, а не асинхронным. Асинхронный это как раз когда потоки без нужды не создаются.
Асинхронность никак не связана с наличие или отсутствием системных потоков. Это всего лишь означает, что мы не блокируем поток исполнения в ожидание результата вычислений, а ставим некую функцию обратного вызова, которую вызовут после готовности данных. А уж как реализована проверка готовности, через опрос некого состояния, ожидание завершения потока или появление сообщения в очереди — это дело десятое.
G>Ну как-бы проблему множества подключений не решает.
А оно и не должно было. Это было описание оптимальной функции копирования файла, а не http-демона. Снова ты не по теме говоришь.
G>Не тоже самое. Ты просто нихрена в этом не понимаешь
Конечно не тоже самое. C# реализация заметно более слабая. ))) Или быть может ты уже готов показать решение той тривиальной тестовой задачки?
Здравствуйте, gandjustas, Вы писали:
G>Ты все зависимости в исходники включи и залей архив сюда. Желательно чтобы в VS собиралось.
Ты похоже совсем не в теме. ))) Папка Boost'a занимает у меня 560 МБ, а wxWidgets 400МБ. Это с lib файлами собранными под gcc. А так и мой примерчик и эти две библиотеки собираются под любым C++11 компилятором и под виндой и под линухом/маком и ещё много где.
Которая работает в том же потоке что и UI. client_stream считывает буковки по WM_CHAR. std::getline — это стандартная функция, которая ничего не знает о корутинах.
Здравствуйте, dalmal, Вы писали:
D>D не нужен. Потому что его нишу плотно занимают C#/Java с тоннами библиотек на любой вкус. И я не вижу у него никакого преимущества, особенно перед C# в технологическом смысле. D>Так что D не нужен. И на смену С++ он придет явно не в этом десятилетии.
C# работает только на венде, а все практические реализации Явы обладают красотой и грациозностью бегемота, пасущегося на лугах Страны Неограниченных Ресурсов.
Кросплатформенный язык общего назначения типа "си с классами", не страдающий при этом врожденной склонностью к онкологическим заболеваниям, безусловно, нужен.
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, gandjustas, Вы писали:
G>>Потрясающе. Если тебе понадобится обрабатывать сотни подключений у тебя будут сотни потоков. Практический лимит потоков на windows — около 200. Но даже при сотне уже все тормозит. При этом потоки будут ничего не делать.
_>Если нам потребуются легковесные потоки, то Boost.Coroutine прямо их и реализует. Т.е. уже без моих хитрых макросов для реализации await/async. Причём делает это несравнимо эффективнее других, обсуждаемых тут, реализаций.
А ты действительно не врубаешься. Копать в сторону IOCP...
И вообще, задача сделать существующий синхронный код асинхронным (с кооперативной многозадачностью и переключениями в момент обращения к стримам), не побоюсь этого слова, на практике редко встречается. Ибо вот так вот одним махом взять и сделать произвольный код асинхронным (при этом не смотря в него) — это нужно быть очень "смелым" и отчаяным — получившиеся "потоки" могут иметь разделяемые изменяемые данные со всеми вытекающими.
Основная проблема, которая, на мой субъективный взгляд, решается через async\await такая: плодить дофига потоков, которые 99% времени только и делают, что ждут IO — непозволительн расточительность ресурсов. Прикол в том, что на уровне ОС IO уже является асинхронным. Но ОС выставляет API синхронный (т.е. это "привычный" IO API, знакомый всем и каждому). А при чтении\записи система блокирует поток до тех пор, пока операция не завершится. Чтобы эти синхронные чтения\записи сделать обратно асинхронными разработчикам приходится плодить потоки. Даже пул потоков не спасает — все его потоки быстро "съедаются" ничегонеделающими операциями ожидания и приходится добавлять в пул все новые и новые потоки. И тут на помощь приходит IOCP! Благодаря ему можно обойтись одним потоком на все операции IO в приложении. Для IO-операции регистрируется callback, который будет вызван в потоке, предназначенным для разгребания очереди IOCP, в момент, когда ОС оповестит о завершении этой операции. Причем в этом callback-е делается не собственно обработка результата операции, а добавление задания (обработчика результата) в дотнетный пул потоков. Т.е. нагрузка на этот поток минимальна, благодаря чему в .Net можно обойтись одним таким IOCP потоком на весь процесс.
Так вот, задача такая: есть множество изначально асинхронных операций (т.е. они не являются результатом "об-асинхронивания" синхронных операций), сделанных в Continuation Passing Style; нужно сделать так, чтобы последовательность их "вызова" выглядела как простой линейный код (плюс, конечно, еще нужны всякие SynchronizationContext, возможность переключения на поток в пуле, назад в вызывающий поток и прочие плюшки). Async\await все это обеспечивает.
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, gandjustas, Вы писали:
G>>Потрясающе. Если тебе понадобится обрабатывать сотни подключений у тебя будут сотни потоков. Практический лимит потоков на windows — около 200. Но даже при сотне уже все тормозит. При этом потоки будут ничего не делать.
_>Если нам потребуются легковесные потоки, то Boost.Coroutine прямо их и реализует. Т.е. уже без моих хитрых макросов для реализации await/async. Причём делает это несравнимо эффективнее других, обсуждаемых тут, реализаций.
Как помогут легковесные потоки если IO блокирующий, как в твоем примере?
_>Асинхронность никак не связана с наличие или отсутствием системных потоков. Это всего лишь означает, что мы не блокируем поток исполнения в ожидание результата вычислений, а ставим некую функцию обратного вызова, которую вызовут после готовности данных. А уж как реализована проверка готовности, через опрос некого состояния, ожидание завершения потока или появление сообщения в очереди — это дело десятое.
Неблокирование путем создания другого потока — хреновая идея. В твоем примере так.
G>>Ну как-бы проблему множества подключений не решает. _>А оно и не должно было. Это было описание оптимальной функции копирования файла, а не http-демона. Снова ты не по теме говоришь.
А чем копирование файла отличается от считывания потока данных из сети и записи на диск?
G>>Не тоже самое. Ты просто нихрена в этом не понимаешь _>Конечно не тоже самое. C# реализация заметно более слабая. ))) Или быть может ты уже готов показать решение той тривиальной тестовой задачки?
Легко.
Было:
void func()
{
var x = read();
write(x);
}
стало:
async Task func()
{
var x = async read();
async write(x);
}
И оно не порождает дополнительные потоки, в отличие от твоего примера.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, gandjustas, Вы писали:
_>>>Теперь показывай решение этой примитивной тестовой задачки на C#. G>>Компилируемый код дай.
EP>Вот полный код
Которая работает в том же потоке что и UI. client_stream считывает буковки по WM_CHAR. std::getline — это стандартная функция, которая ничего не знает о корутинах.
Ты как-то забыл написать что основная асинхронность создается тут:
Здравствуйте, Pzz, Вы писали:
Pzz>Здравствуйте, dalmal, Вы писали:
D>>D не нужен. Потому что его нишу плотно занимают C#/Java с тоннами библиотек на любой вкус. И я не вижу у него никакого преимущества, особенно перед C# в технологическом смысле. D>>Так что D не нужен. И на смену С++ он придет явно не в этом десятилетии.
Pzz>C# работает только на венде, а все практические реализации Явы обладают красотой и грациозностью бегемота, пасущегося на лугах Страны Неограниченных Ресурсов.
Думаю стоит глянуть на Mono и Xamarin, перед тем как толкать такие утверждения. Сейчас спектр систем, где заработает код на C#, примерно такой же, как у java и у обоих гораздо шире, чем у C++.
Pzz>Кросплатформенный язык общего назначения типа "си с классами", не страдающий при этом врожденной склонностью к онкологическим заболеваниям, безусловно, нужен.
Практика показывает что эту нишу занял javascript.
Здравствуйте, gandjustas, Вы писали:
EP>>Которая работает в том же потоке что и UI. client_stream считывает буковки по WM_CHAR. std::getline — это стандартная функция, которая ничего не знает о корутинах. G>Ты как-то забыл написать что основная асинхронность создается тут: G>
А выделенное ты не читал?
G>То есть это очень частный случай. Сколько у тебя таких частных случаев в программе будет? 100? Задолбаешься поддерживать.
Что значит особый случай? Точно также можно завернуть tcp_stream. При этом код использующий этот поток не поменяется, а в случае с C# await — нужно будет патчить весь call-stack
Причём большую часть boilerplate'а необходимого для заворачивания в корутины, легко вынести в библиотеку
G>Кроме того все это выполняется через глобальную переменную. Запустить два таких считывания уже не выйдет.
Это минимальный пример показывающий как вызов std::getline может работать с асинхронным потоком. Если будет async_tcp_stream — то будет хоть 100k соединений
G>Ты считаешь что такое "решение" способно тягаться с async\await в C#?
, только получается намного мощнее, так как не ограничен одним уровнем. А вот ты на C# await повторить пример с getline-like никак не сможешь. Разве не очевидно что мощнее
Здравствуйте, artelk, Вы писали:
A>И вообще, задача сделать существующий синхронный код асинхронным (с кооперативной многозадачностью и переключениями в момент обращения к стримам), не побоюсь этого слова, на практике редко встречается.
The example above used gevent.socket for socket operations. If the standard socket module was used it would took it 3 times longer to complete because the DNS requests would be sequential. Using the standard socket module inside greenlets makes gevent rather pointless, so what about module and packages that are built on top of socket?
That’s what monkey patching for. The functions in gevent.monkey carefully replace functions and classes in the standard socket module with their cooperative counterparts. That way even the modules that are unaware of gevent can benefit from running in multi-greenlet environment.
#!/usr/bin/python
# Copyright (c) 2009 Denis Bilenko. See LICENSE for details.
"""Spawn multiple workers and wait for them to complete"""
urls = ['http://www.google.com', 'http://www.yandex.ru', 'http://www.python.org']
import gevent
from gevent import monkey
# patches stdlib (including socket and ssl modules) to cooperate with other greenlets
monkey.patch_all()
import urllib2
def print_head(url):
print'Starting %s' % url
data = urllib2.urlopen(url).read()
print'%s: %s bytes: %r' % (url, len(data), data[:50])
jobs = [gevent.spawn(print_head, url) for url in urls]
gevent.joinall(jobs)
A>Так вот, задача такая: есть множество изначально асинхронных операций (т.е. они не являются результатом "об-асинхронивания" синхронных операций), сделанных в Continuation Passing Style; нужно сделать так, чтобы последовательность их "вызова" выглядела как простой линейный код (плюс, конечно, еще нужны всякие SynchronizationContext, возможность переключения на поток в пуле, назад в вызывающий поток и прочие плюшки). Async\await все это обеспечивает.
Обеспечивает — и довольно неплохо. Но, тут два основных момента:
1. await-like реализуется поверх stackful coroutine, с точно таким же синтаксисом
int bar(int i)
{
// await is not limited by "one level" as in C#auto result = await async([i]{ return reschedule(), i*100; });
// attaches rest of method as continuation into .then of returned futurereturn result + i*10;
}
2. С помощью корутин решаются такие задачи, которые не под силу C# await. И не надо разводить демагогию на тему "это не нужно/это редко встречается".
Здравствуйте, gandjustas, Вы писали:
G> Сейчас спектр систем, где заработает код на C#, примерно такой же, как у java и у обоих гораздо шире, чем у C++.
C++ работает и на десктопах, и на планшетах, и на телефонах (разве что кроме первых версий windows phone), и на 8-битных контролерах, и даже внутри javascript
Здравствуйте, artelk, Вы писали:
A>И вообще, задача сделать существующий синхронный код асинхронным (с кооперативной многозадачностью и переключениями в момент обращения к стримам), не побоюсь этого слова, на практике редко встречается. Ибо вот так вот одним махом взять и сделать произвольный код асинхронным (при этом не смотря в него) — это нужно быть очень "смелым" и отчаяным — получившиеся "потоки" могут иметь разделяемые изменяемые данные со всеми вытекающими.
Задача использовать существующие библиотеки в современном коде, а не переписывать их самим — это редко встречаемая? ) Нуну)))
A>Основная проблема, которая, на мой субъективный взгляд, решается через async\await такая: плодить дофига потоков, которые 99% времени только и делают, что ждут IO — непозволительн расточительность ресурсов.
Что-то я не понял где вообще может встретиться подобная задачка, даже если бы это и не было роскошью. "Дофига потоков" нам может понадобиться в серверных решениях со множеством клиентов (например http-демон). Но даже если мы и предположим сделаем такую неразумную вещь, как реализацию высоконагруженного сервера через системные потоки, то они при этом явно не будут долго сидеть в ожидание — будут отрабатывать запрос и завершаться. Если же мы говорим о каких-то десктопных приложениях, то там действительно бывают ничегонеделающие потоки, но откуда там потребность в тысячах таких? Так что получается что вся эта твоя речь — это спор с голосами в твоей же голове. )))
A>Прикол в том, что на уровне ОС IO уже является асинхронным. Но ОС выставляет API синхронный (т.е. это "привычный" IO API, знакомый всем и каждому).
Вообще то выставляются оба варианта.
A>А при чтении\записи система блокирует поток до тех пор, пока операция не завершится. Чтобы эти синхронные чтения\записи сделать обратно асинхронными разработчикам приходится плодить потоки. Даже пул потоков не спасает — все его потоки быстро "съедаются" ничегонеделающими операциями ожидания и приходится добавлять в пул все новые и новые потоки.
Так что же это за софт такой, который плодит тысячи ничегонеделающих потоков? Можно пример? )
A>И тут на помощь приходит IOCP! Благодаря ему можно обойтись одним потоком на все операции IO в приложении. Для IO-операции регистрируется callback, который будет вызван в потоке, предназначенным для разгребания очереди IOCP, в момент, когда ОС оповестит о завершении этой операции. Причем в этом callback-е делается не собственно обработка результата операции, а добавление задания (обработчика результата) в дотнетный пул потоков. Т.е. нагрузка на этот поток минимальна, благодаря чему в .Net можно обойтись одним таким IOCP потоком на весь процесс.
А причём тут дотнет то? Это всё есть в обычном win32 апи с древних времён. Да и в posix есть свой аналог.
A>Так вот, задача такая: есть множество изначально асинхронных операций (т.е. они не являются результатом "об-асинхронивания" синхронных операций), сделанных в Continuation Passing Style; нужно сделать так, чтобы последовательность их "вызова" выглядела как простой линейный код (плюс, конечно, еще нужны всякие SynchronizationContext, возможность переключения на поток в пуле, назад в вызывающий поток и прочие плюшки). Async\await все это обеспечивает.
Классно. ))) Правда это уже всё работает не одно десятилетие без всякого .net'а... Ну разве что кроме линеаризации такого кода. И вот обсуждаемые тут Boost.Coroutine как раз и реализуют эту самую линеаризацию для C++ кода. Причём более эффективно (и по возможностям и по быстродействию) чем .net вариант.
Здравствуйте, gandjustas, Вы писали:
G>Как помогут легковесные потоки если IO блокирующий, как в твоем примере?
А причём тут легковесные потоки и мой пример? У меня там, как ты видел, обычное десктопное приложение (окошко с кнопкой), а не сервер с тысячами подключений. Зачем мне там такие потоки?
G>Неблокирование путем создания другого потока — хреновая идея. В твоем примере так.
Ну покажи в чём хреновость. )
G>А чем копирование файла отличается от считывания потока данных из сети и записи на диск?
Не важно что и куда, а важно сколько операций параллельно. Реализация через пару системных потоков будет максимально эффективной и для сети (собственно там вообще идеально, т.к. устройства точно разные). Однако если нам понадобится много таких параллельных скачиваний, то эффективность будет ухудшаться с их увеличением. Так что начиная с какого-то момента уже выгоднее использовать другую модель (легковесные потоки например).
_>>Конечно не тоже самое. C# реализация заметно более слабая. ))) Или быть может ты уже готов показать решение той тривиальной тестовой задачки? G>Легко.
В этом случае если внутри "stream >> x" произойдёт yield, то инварианты могут быть поломаны. Но, если есть такой код — то ты его и на обычных потоках не сможешь запустить, корутины тут не причём
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, gandjustas, Вы писали:
EP>>>Которая работает в том же потоке что и UI. client_stream считывает буковки по WM_CHAR. std::getline — это стандартная функция, которая ничего не знает о корутинах. G>>Ты как-то забыл написать что основная асинхронность создается тут: G>>
EP>А выделенное ты не читал?
Читал, а толку то? Этот подход решает очень узкую задачу — асинхронное чтение буковок. Это можно и с меньшим количеством кода сделать.
На обобщенный метод превращения синхронного кода в асинхронный не тянет.
G>>То есть это очень частный случай. Сколько у тебя таких частных случаев в программе будет? 100? Задолбаешься поддерживать.
EP>Что значит особый случай? Точно также можно завернуть tcp_stream.
Надо очень хорошо знать как код работает внутри, чтобы делать такую асинхронность. А как комбинировать такие асинхронные вызовы не то что не очевидно, а вообще непонятно как.
EP>При этом код использующий этот поток не поменяется, а в случае с C# await — нужно будет патчить весь call-stack
А зачем это делать? Преобразование синхронного кода в асинхронный — механическая операция, легко автоматизируется при желании. Я даже для стримов на roslyn такое переписывание делал.
EP>Причём большую часть boilerplate'а необходимого для заворачивания в корутины, легко вынести в библиотеку
Почему до сих пор никто не сделал? Видимо не так просто, ибо более чем востребовано в наше время.
G>>Кроме того все это выполняется через глобальную переменную. Запустить два таких считывания уже не выйдет.
EP>Это минимальный пример показывающий как вызов std::getline может работать с асинхронным потоком.
Проблема в том, что они показывает только вызов std::getline. Покажи как сделать тоже самое с вводом-выводом на IOCP.
EP>Если будет async_tcp_stream — то будет хоть 100k соединений
Для начала тебе надо будет async io реализовать на IOCP.
G>>Ты считаешь что такое "решение" способно тягаться с async\await в C#?
EP>await-like синтаксис легко реализуется поверх корутин
, только получается намного мощнее, так как не ограничен одним уровнем.
Каким одним уровнем? ты о чем?
G>>А вот ты на C# await повторить пример с getline-like никак не сможешь. Разве не очевидно что мощнее
А зачем его повторять? Я же говорю что просто дописываешь async\await где можно код становится асинхронным. Даже понимать не надо как код работает.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, gandjustas, Вы писали:
G>> Сейчас спектр систем, где заработает код на C#, примерно такой же, как у java и у обоих гораздо шире, чем у C++.
EP>C++ работает и на десктопах, и на планшетах, и на телефонах (разве что кроме первых версий windows phone), и на 8-битных контролерах, и даже внутри javascript