Здравствуйте, Ikemefula, Вы писали:
I>На stackfull короутинах здесь не было ни одного примера асинхронщины. Ни одного, зато есть вопли, как всё просто и шоколадно. Есть мой пример с message pump — допили его и продемонстрируй асинхронщину
Да легко. В начале для наглядности исходный (синхронный блокирующий) код:
Насколько я понимаю (хотя я не особо разбирался — может поправишь меня?), написать подобный однопоточный код на C# без модификации функции getline и класса wistream нереально. Ну а тут как видишь без проблем пишется.
Ну и реализации async классов (по сути всё тоже самое что и в моих предыдущих примерах тут, только чуть формальнее):
class AsyncPool{
using Coro=boost::coroutines::coroutine<void()>;
list<Coro> coro_list;
public:
using This=Coro::caller_type;
template<typename L> void Add(L l) {coro_list.emplace_back(l);}
void RunAll()
{
for(auto& c: coro_list) c();
coro_list.remove_if([](const Coro& c) {return !c;});
}
}
class АsyncBuf: public wstreambuf{
AsyncPool::This& this_async;
wstreambuf* sync_buf;
public:
АsyncBuf(AsyncPool::This& c, wstreambuf* sb): this_async(c), sync_buf(sb){}
wchar_t getc()//псевдокод, т.к. я не помню реальный api у std::streambuf
{
while(!sync_buf->is_avail()) this_async();
return sync_buf->getc();
}
};
I>Пока что очевидно, что вызов любого метода из message pump приведет замораживанию приложения. Что бы это забороть, надо явно делать короутинами вообще всё, в т.ч. и message pump. То есть на ровном месте педалить и приседать с кооперативной многозадачностью.
Как видишь ничего подобного. )))
I>Смешно — короутины есть в современных OS с незапамятных времен и как то это никак не сказалось на асинхронщине
Потому как реально эта самая асинхронщина (не путаем с нормальным многопоточным кодом) очень мало где нужна.
Да, кстати, для сравнения можно ещё и многопоточный вариант твоего кода представить:
AsyncPool async_pool;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR lpCmdLine, int nCmdShow)
{
AttachConsole();
MSG msg;
while(GetMessage(&msg, NULL, 0, 0)>0){
async_pool.RunAll();
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
void SomeMessageHandler()
{
async_pool.Add([=](AsyncPool::This& this_async){//эти две строки можно для красотыauto r=await_async(this_async, [&](){//запрятать в макрос :D
wstring buf;
getline(wcin, buf);
return buf;
});
wcout<<L"Эхо: "<<r;
});
}
Но это скучный (т.к. он на C# без проблем пишется , да и некорректный (если несколько getline'ов будут параллельно бороться за ввод пользователя) вариант.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Если всё что нужно это сделать фоновую скачку файла, то вполне могу себе представить ситуацию, где даже при наличии await'а — вместо него будет использоваться явный код с .then'ом, указанием ui контекста и т.п., ибо очевидно что у await'а есть свой порог вхождения, а код не только пишется, но ещё и обслуживается, причём зачастую разными людьми. Например тот же Ikemefula, который бьёт себя пяткой в грудь что заменит целый взвод азиатов, уже неделю не может понять как можно использовать stackful coroutine. Тогда чего уж там говорить о "типичных" программистах.
Вот вот. И дело даже не только в тех случаях, когда не понимают. Даже для всё знающего профессионала более наглядный (где чётко видно в каком потоке какой код исполняется) код может приводить к уменьшению случайных ошибок.
На мой вкус подобные техники реально полезны в двух случаях:
1. Написание библиотек, которые должны работать в двух режимах (синхронный и асинхронный) — тут естественно это должно быть замечательным решением без избыточного кода. Хотя пока реальных примеров не видел.
2. Случаи когда нам требуются много тысяч параллельных задач.
Может и ещё что-то есть, что сейчас в голову не пришло. А во всех остальных задачах на мой вкус проще, нагляднее и эффективнее использовать классическое многопоточное программирование.
В принципе работать будет но могут быть осложнения:
1. Если сообщения не приходят достаточно часто, то и вызов async_pool.RunAll(); будет необоснованно задерживаться.
2. Вызов async_pool.RunAll(); может долго не отдавать управление, если асинхронные операции долго не завершаются. Он же там ждёт на select() каком-нибудь.
Строго однопоточное и неблокирующееся решение состоит в использовании MsgWaitForMultipleObjects() под виндой и использовании хэндлера ConnectionNumber() под X Window.
А правильное решение состоит в использовани отдельного потока для GUI.
_>Насколько я понимаю (хотя я не особо разбирался — может поправишь меня?), написать подобный однопоточный код на C# без модификации функции getline и класса wistream нереально. Ну а тут как видишь без проблем пишется.
То есть, снова появляется или функция или макрос, то есть, наружу торчат асинхронные кишки ?
_>Ну и реализации async классов (по сути всё тоже самое что и в моих предыдущих примерах тут, только чуть формальнее): _>
_>class AsyncPool{
_> using Coro=boost::coroutines::coroutine<void()>;
_> list<Coro> coro_list;
_>public:
_> using This=Coro::caller_type;
_> template<typename L> void Add(L l) {coro_list.emplace_back(l);}
_> void RunAll()
_> {
_> for(auto& c: coro_list) c();
_> coro_list.remove_if([](const Coro& c) {return !c;});
_> }
_>}
Где вызов RunAll ?
_>class АsyncBuf: public wstreambuf{
_>AsyncPool::This& this_async;
_>wstreambuf* sync_buf;
_>public:
_> АsyncBuf(AsyncPool::This& c, wstreambuf* sb): this_async(c), sync_buf(sb){}
_> wchar_t getc()//псевдокод, т.к. я не помню реальный api у std::streambuf
_> {
_> while(!sync_buf->is_avail()) this_async();
_> return sync_buf->getc();
_> }
_>};
_>
И снова не совсем ясно,
1 как вернется управление к моему message pump
2 когда будет получать управление диспетчер короутин
I>>Пока что очевидно, что вызов любого метода из message pump приведет замораживанию приложения. Что бы это забороть, надо явно делать короутинами вообще всё, в т.ч. и message pump. То есть на ровном месте педалить и приседать с кооперативной многозадачностью.
_>Как видишь ничего подобного. )))
Я пока что вижу в лучшем случае надо писать ровно то же, только руками строить короутину. В C# нужно писать async-await, а у тебя макры
I>>Смешно — короутины есть в современных OS с незапамятных времен и как то это никак не сказалось на асинхронщине _>Потому как реально эта самая асинхронщина (не путаем с нормальным многопоточным кодом) очень мало где нужна.
Да как бы наоборот — асинхронщина нужна везде и всегда.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, Ikemefula, Вы писали:
I>>>>Возьми любой мой пример, я ажно пять вариантов привел. Только должно быть явно обозначено, как будет вызываться message pump. И message pump именно мой, а не просто какой то эвентлуп для короутин. EP>>>Ты понимаешь что такое полностью рабочий пример? Это не те огрызки которые ты тут приводил. I>>Внятно ответь на вопросы I>>1 — как получит управление МОЙ message pump. I>>2 — как message pump передаст управление короутине когда процессинг закончится.
EP>Зависит от того какую асинхронную операцию и как (по какому событию) ты собрался выполнять, поэтому и прошу привести полный пример.
case WM_USER: // все прост, никаких фокусов
textBox.text += await handler();
break;
операция примерно такая, аналог на с# чз AsyncCallback, AsyncResult и тд.
string async handler()
{
var http = new Http("some url");
var result = await Task.FromBeginEnd(asyncCallback)=>http.BeginRead(asyncCallback), (asyncResult)=>http.EndRead(asyncResult) );
return string;
}
I>>Условно считаем так — есть message pump и есть ровно один хандлер который вызывается этим message pump. От тебя требуется показать что нибудь предельно простое, асинхронное, которое реализовано унутре этого хандлера.
EP>Видимо конкретного кода я не дождусь. Давай тогда так: EP>Я только добавивлю include корутин, и сделаю трансформацию описанную выше внутри task. EP>Такой пример подойдёт?
Не ясно, что за трансформацию ты собираешься делать внутри таска. Если всю асинхронщину, то её надо делать в хандлере.
I>>Пока что ты продемонстрировал замерзание UI.
EP>Где?
Твой код останавливает message pump и нигде не было показано как этот памп будет продолжен.
Здравствуйте, Ikemefula, Вы писали:
EP>>Зависит от того какую асинхронную операцию и как (по какому событию) ты собрался выполнять, поэтому и прошу привести полный пример. I>
I>case WM_USER: // все прост, никаких фокусов
I> textBox.text += await handler();
I> break;
I>
покажи полный код
I>Не ясно, что за трансформацию ты собираешься делать внутри таска. Если всю асинхронщину, то её надо делать в хандлере.
Здравствуйте, Mazay, Вы писали:
M>В принципе работать будет но могут быть осложнения: M>1. Если сообщения не приходят достаточно часто, то и вызов async_pool.RunAll(); будет необоснованно задерживаться.
Ага, точно, надо MsgWaitForMultipleObjects. Ну собственно я то в любом случае не стал бы писать такой код, а это было по запросу Ikemefula на его примере. Я то написал бы просто
M>2. Вызов async_pool.RunAll(); может долго не отдавать управление, если асинхронные операции долго не завершаются. Он же там ждёт на select() каком-нибудь.
Нет, там нет ничего блокирующего вообще.
M>А правильное решение состоит в использовани отдельного потока для GUI.
Нуу да, правда формулировка какая-то нестандартная. Чаще же основной поток как раз для GUI и куча отдельных для какой-то работы.
Здравствуйте, Ikemefula, Вы писали:
I>То есть, снова появляется или функция или макрос, то есть, наружу торчат асинхронные кишки ?
Ну так кишки то по любому есть и в C++ и в C#. Фишка данного кода в том, что мы используем обычную синхронную блокирующую функцию getline в асинхронном однопоточном режиме без всякой её модификации.
I>Где вызов RunAll ?
В главном цикле же.
I>И снова не совсем ясно, I>1 как вернется управление к моему message pump
Handler возвращает управление немедленно при любом раскладе.
I>2 когда будет получать управление диспетчер короутин
Когда будут пробегать сообщения в главном цикле. В принципе Mazay уже правильно указал, что просто GetMessage тогда тут некорректно и надо поправить главный цикл чтобы он крутился не только по обычным сообщениям.
I>Я пока что вижу в лучшем случае надо писать ровно то же, только руками строить короутину. В C# нужно писать async-await, а у тебя макры
Хыхыхы, если речь конкретно про этот код, то его аналог на C# вообще не написать никак. Так что до сравнения стилистических нюансов дело как бы вообще не должно доходить. )))
I>Да как бы наоборот — асинхронщина нужна везде и всегда.
Ну это смотря что под ней подразумевать... Например такой код
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>покажи полный код
Это будет 100-200кб которые я выдеру из декомпиленой сборки. Тебя это устроит ?
Можешь сделать все что угодно, ожидание UI запущеного приложение, ожидание какого либо объекта ядра — все что угодно, только не бустовскую хрень, где это все упаковано хрен знает куда.
EP>task переписывается на корутины без каких-либо изменений остального кода
Мне нужно что бы ты показал
1 где будет ожидание результата
2 как управление вернется в message pump
2 как управление будет передаваться в твой шедулер или короутину
Здравствуйте, Ikemefula, Вы писали:
EP>>task переписывается на корутины без каких-либо изменений остального кода I>Мне нужно что бы ты показал I>1 где будет ожидание результата
Тут ожидание события SIZE_MINIMIZED.
I>2 как управление вернется в message pump
После вызова async_something
I>2 как управление будет передаваться в твой шедулер или короутину
По событию, так же как в примере с task выше.
Подходит?
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>>>task переписывается на корутины без каких-либо изменений остального кода I>>Мне нужно что бы ты показал I>>1 где будет ожидание результата
EP>Тут ожидание события SIZE_MINIMIZED.
Не надо такое.
I>>2 как управление вернется в message pump
EP>После вызова async_something
I>>2 как управление будет передаваться в твой шедулер или короутину
EP>По событию, так же как в примере с task выше. EP>Подходит?
Сделай ожидание какого нибудь объекта ядра или еще чего навроде.
I>>2 когда будет получать управление диспетчер короутин _>Когда будут пробегать сообщения в главном цикле. В принципе Mazay уже правильно указал, что просто GetMessage тогда тут некорректно и надо поправить главный цикл чтобы он крутился не только по обычным сообщениям.
Шота у тебя и EP показания не сходятся
I>>Я пока что вижу в лучшем случае надо писать ровно то же, только руками строить короутину. В C# нужно писать async-await, а у тебя макры
_>Хыхыхы, если речь конкретно про этот код, то его аналог на C# вообще не написать никак. Так что до сравнения стилистических нюансов дело как бы вообще не должно доходить. )))
Цель, понятное дело, написать так как нельзя в сишарп а не решить конкретную проблему ?
I>>Да как бы наоборот — асинхронщина нужна везде и всегда.
_>Ну это смотря что под ней подразумевать...
Да все как везде — скачивание, обращение к базе, устройству и тд и тд и тд и тд и тд и тд и тд
Здравствуйте, Ikemefula, Вы писали:
I>>>1 где будет ожидание результата EP>>Тут ожидание события SIZE_MINIMIZED. I>Не надо такое.
Почему? Чем отличается от "ожидания результата" в данном контексте?
I>>>2 как управление будет передаваться в твой шедулер или короутину EP>>По событию, так же как в примере с task выше. EP>>Подходит? I>Сделай ожидание какого нибудь объекта ядра или еще чего навроде.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
I>>>>1 где будет ожидание результата EP>>>Тут ожидание события SIZE_MINIMIZED. I>>Не надо такое.
EP>Почему? Чем отличается от "ожидания результата" в данном контексте?
Я чуток запутался, по WM_SIZE будет таск запускаться, тогда нормально. Где будет ожидание результата в task ?
Здравствуйте, alex_public, Вы писали:
_>Ага, точно, надо MsgWaitForMultipleObjects. Ну собственно я то в любом случае не стал бы писать такой код, а это было по запросу Ikemefula на его примере. Я то написал бы просто
Ага и еще тебе придется таймер подключать к этому.
M>>2. Вызов async_pool.RunAll(); может долго не отдавать управление, если асинхронные операции долго не завершаются. Он же там ждёт на select() каком-нибудь.
_>Нет, там нет ничего блокирующего вообще.
Теперь мы пришли к выводу, что надо патчить Message Pump.
::MessageBox() — вот эта функция похерит всю твою асинхронность по той простой причине, что запускает message pump и ничего не знает про твой RunAll
Теперь задачка посложнее, представь себе, что у тебя нет доступа к Message Pump, как заставить работать твою асинхронность ?
И похоже, что ваша хваленая асинхронность будет дико лагать, т.к. сильно зависит от частоты эвентов в системе и целой кучи других факторов.
Во втором statement'е таск запускает асинхронную обработку, и передаёт лямбду-продолжение как параметр, completion callback — т.е. что делать, когда это something (например download) произойдёт.
Здравствуйте, Ikemefula, Вы писали:
I>Выдели жирным, взял из твоего кода этот самый главный цикл
Это ты скопиривал из синхронного кода, а не асинхронного. )))
I>Шота у тебя и EP показания не сходятся
Так он там тогда другую модельку обсуждал.
I>Цель, понятное дело, написать так как нельзя в сишарп а не решить конкретную проблему ?
Вообще то в последнее время дискуссия свернула к вопросу превосходства stackfull coroutines над остальными вариантами. И этот код отлично демонстрирует один из нюансов превосходства: при переходе с синхронного кода на асинхронный нам не требуется менять весь код в стеке вызова, а правится только самый высший и самый низший уровень.
I>Да все как везде — скачивание, обращение к базе, устройству и тд и тд и тд и тд и тд и тд и тд
Ну я же вроде привёл конкретный пример... Снова отмазываемся? )))
Если да, то тогда я подпишусь под утверждением что она нужна везде... Правда я привык называть такое самым обычным многопоточным программированием... Но лишнее название не помеха. )))
Если же нет, то тогда формулируй что же такое "асинхронщина" и будем уже разбираться по формулировке нужно оно где-то или нет...
Здравствуйте, Ikemefula, Вы писали:
I>Ага и еще тебе придется таймер подключать к этому.
Нет, если использовать MsgWaitForMultipleObjects, то таймер не нужен. Кстати, там даже есть вариант ждать событий с вводом в консоль, так что вообще оптимально для данной задачки выходит.
Ну или же можно действительно таймер сделать и тогда можно оставить старый GetMessage. Но это уже не так оптимально.
I>Теперь мы пришли к выводу, что надо патчить Message Pump. I>::MessageBox() — вот эта функция похерит всю твою асинхронность по той простой причине, что запускает message pump и ничего не знает про твой RunAll I>Теперь задачка посложнее, представь себе, что у тебя нет доступа к Message Pump, как заставить работать твою асинхронность ? I>И похоже, что ваша хваленая асинхронность будет дико лагать, т.к. сильно зависит от частоты эвентов в системе и целой кучи других факторов.
Эээ ты похоже бредишь. ))) Цель всей этой нашей асинхронщины (что в многопоточном, что в однопоточном варианте) в том, что сделать какие-то дела на стороне и вернуть результат в UI поток. Так что в этой задачке при любой схеме (однопоточной/многопоточной) и при любом языке программирования нам надо будет ждать пока UI поток не освободится для обработки асинхронного результата.
Если же нам можно обрабатывать результат не в UI потоке, то это совсем другая задача становится, которая решается вообще элементарно, но вроде как к "асинхронщине" уже совсем никакого отношения не имеет.
Здравствуйте, alex_public, Вы писали:
I>>Цель, понятное дело, написать так как нельзя в сишарп а не решить конкретную проблему ?
_>Вообще то в последнее время дискуссия свернула к вопросу превосходства stackfull coroutines над остальными вариантами. И этот код отлично демонстрирует один из нюансов превосходства: при переходе с синхронного кода на асинхронный нам не требуется менять весь код в стеке вызова, а правится только самый высший и самый низший уровень.
Самый высший — это надо контролировать глобальный цикл выборки сообщений или чтото навроде. Этой возможности может и не быть. Даже так — в общем случае её нет. Любое модальное окно порушит всю твою стройную идею.
I>>Да все как везде — скачивание, обращение к базе, устройству и тд и тд и тд и тд и тд и тд и тд
_>Ну я же вроде привёл конкретный пример... Снова отмазываемся? )))
_>Вот это _>