Russian Qt Forum
Апрель 19, 2024, 03:10 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 2 [3] 4   Вниз
  Печать  
Автор Тема: Корутины (модное слово)  (Прочитано 21941 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #30 : Сентябрь 29, 2020, 13:19 »

Ну хорошо, вот задача соседней темы которая прямо напрашивается на корутину. Получив MousePressed мне нужно уходить в событийный цикл до тех пор пока не получу MouseMoved или MouseReleased, после этого продолжить обработку MousePressed

Как это сделать?
« Последнее редактирование: Сентябрь 30, 2020, 15:39 от Igors » Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #31 : Сентябрь 29, 2020, 14:10 »

по аналогии с подпиской на сигнал, только вместо коннекта надо вешать эвент фильтр из пробуждать корутину из него
детали мне неведомы я еще ни одной корутины на с++ не писал пока только читаю портянки кода других людей.
выкладывайте, будет интересно посмотреть
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #32 : Сентябрь 29, 2020, 15:27 »

выкладывайте, будет интересно посмотреть
Вы переоцениваете мой энтузиазм Улыбающийся Я еще и до c++17 не дополз, а о с++20 нечего и говорить

по аналогии с подпиской на сигнал, только вместо коннекта надо вешать эвент фильтр из пробуждать корутину из него
Чего ж из фильтра-то? Напрашивается так
Код
C++ (Qt)
while (true)  {   // хз умеет ли возвращать
co_await IsMouseStillDown();
if (releaseMouseFlag) break;
 
А прыжки взад уже из фильтра. Но как я попаду в eventLoop из IsMouseStillDown ? Если тем же processEvents то все тот же головняк, "выйгрышь" никакой.

Да, и где Ваш молодой интересный друг? Для которого все "элементарно" (что кстати совсем не синоним "просто" как объяснял мне профессиональный математик).
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #33 : Сентябрь 29, 2020, 20:58 »

Очень годная лекция которая в целом отвечает на вопрос "а чо так сложна" https://www.youtube.com/watch?v=R_gZQJC-uv0
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #34 : Сентябрь 30, 2020, 15:38 »

Очень годная лекция которая в целом отвечает на вопрос "а чо так сложна" https://www.youtube.com/watch?v=R_gZQJC-uv0
Не очень ясно. Ну ладно, вот (редкий) светлый момент (аттач). Верно ли я понимаю что

auto t = foo();  // напечатает "Hello"
t.resume();       // напечатает "World"
t.resume();       // напечатает "World" еще раз

?

« Последнее редактирование: Сентябрь 30, 2020, 15:39 от Igors » Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #35 : Сентябрь 30, 2020, 15:53 »

Нет, это он приводит пример корутины которая не начинает сразу "рваться в бой" а стопается сразу при входе в неё (https://youtu.be/R_gZQJC-uv0?t=2047)
Код:
auto initial_suspend() { return std::suspend_always(); }

Поэтому чтобы начать ее исполнение надо позвать resume() в первый раз. В примере который мы рассматривали выше (с сигналами) initial_suspend не тормозил корутину.
Зачем такое нужно? Например "ленивые" вычисления - вы отдельно создали корутину, а исполнили ее код когда-нибудь потом, когда она реально понадобится.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #36 : Сентябрь 30, 2020, 16:18 »

Код:
auto t = foo();  // не делает ничего, просто создает объект корутины (окей, это относительно тяжелая операция)
t.resume();       // напечатает "hello"
t.resume();       // напечатает "World"
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #37 : Октябрь 01, 2020, 12:15 »

Да, лектор поясняет этот момент через неск секунд после того как я "иссяк" Улыбающийся Сегодня досмотрел до конца, во второй раз пошло значительно легче.

Выходит "корутины" - это по сути возможность выполнить не всю ф-цию, а ее часть/кусок. Само по себе это никак не связано с асинхронностью, фоновым выполнением и.т.п. Еще раз тот же кусок кода
Код
C++ (Qt)
while (true) {
  qDebug() << "entering loop";
  co_await qtcoro::make_awaitable_signal(changeTimer, &QTimer::timeout);
  qDebug() << "awaited";
  cr.changeColor();
}
Да, "не блокируется", просто в строке co_await происходит return (специфический), а строка awaited выполнится когда корутину опять позовут, кто и когда - дело вызывающего.

Вернемся к драгу. Получается ф-ция драга должна быть корутиной (не очень удобно, это член класса), а вызывать ее должны все обработчики "пожираемых" событий, напр
Код
C++ (Qt)
bool MyFilter::eventFilter( QObject * obj, QEvent * e )
{
  switch (e->type()) {
    ...
    case QEvent::MouseMoved:
      m_drag.resume();
      return true;
   ...
  }
}
Где m_drag - член класса фильтр, хендл на корутину, как его делать - не знаю, найду подобное и перепишу. Теперь сам драг
Код
C++ (Qt)
void MyDrag( void )
{
...
while (true) {
 ...
 co_yield;    // ждем пока не вызовет одно из событий
 
// тело драга
 QPoint pt = GetMousePosition();
 if (pt == oldPt) continue;
 DoDrag(pt);
 oldPt = pt;
}
}
}
Верно ли я мыслюсь?  Если да, то как передать в корутину QEvent * e ?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #38 : Октябрь 05, 2020, 11:01 »

По поводу лекции - это ужасно. Во-первых, ничего не дается "под запись", чуть прощелкал, потерял нить - и пиши пропало Плачущий Хотя я сам записывать никогда не любил, но все же... на момент лекции человек может совсем не гореть жаждой знаний, отягощен др впечатлениями и.т.п. И он остается "за бортом" - это не есть хорошо.

Во-вторых, неплохо бы сразу пояснить о чем речь вообще, "за что боремся". Отот пример (скрыншот выше) надо дать сразу - мы можем "входить в ф-цию", и это круто.  Не понимая этого главного я долго "плавал" не врубаясь о чем речь. Еще и бросает издевательские фразы типа "пользоваться корутинами очень легко". Ну и показал бы если "легко". Хотя это совсем не так, вот примерчик кончился, первая самостоятельная попытка с драгом - и тишина.

Ладно, посмотрим как получить хендл чтобы звать resume, без этого не обойтись. Лектор говорит
Цитировать
Нужно реализовать простейший тип resumable вот для этого
А нафиг оно мне надо, что-то там реализовывать? Готового нету, что ли? Мне юзать надо, а не вдаваться в нюансы Улыбающийся
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #39 : Октябрь 05, 2020, 15:41 »

По поводу лекции - это ужасно. Во-первых, ничего не дается "под запись", чуть прощелкал, потерял нить - и пиши пропало Плачущий Хотя я сам записывать никогда не любил, но все же... на момент лекции человек может совсем не гореть жаждой знаний, отягощен др впечатлениями и.т.п. И он остается "за бортом" - это не есть хорошо.

Чувак же неоднократно говорил что запись будет на ютубе, что непонятно, можно пересмотреть. Я вообще смотрю видео на скорости 1.5 потому что люди оч медленно говорят. А почитать очередной слайд можно и на паузу нажать.

Во-вторых, неплохо бы сразу пояснить о чем речь вообще, "за что боремся".
За что борются классы в с++? За что борются шаблоны? Разработчики шаблонов вредил могли подумать о том что люди будут использовать их в метапрограммировании, они задумывались как "генерики".
Примеров использования корутин масса, некоторые он перечислил - генераторы, бесконечные последовательности, ленивые вычисления. Многое из этого есть в питоне, да даже в убогом TCL есть корутины. Серьезно, пощупайте питон, генераторы, async/await.
Основная идея - это "линеаризация" асинхронного кода так чтобы он выглядел синхронно - на каждом "синхронном" вызове мы просто уходим в эвентлуп и возвращаемся когда пришло нужно событие. Вместо лапши хендлеров эвентов, коллбэков или сигнал-слотов получаем простую последовательность действий как если бы мы писали синхронно. Но ожиданий и блокировок нет - код максимально производительный. Всё это требует поддержки библиотеками, конечно, boost asio и QTcpSocket придется переписать.

Отот пример (скрыншот выше) надо дать сразу - мы можем "входить в ф-цию", и это круто.  Не понимая этого главного я долго "плавал" не врубаясь о чем речь. Еще и бросает издевательские фразы типа "пользоваться корутинами очень легко". Ну и показал бы если "легко". Хотя это совсем не так, вот примерчик кончился, первая самостоятельная попытка с драгом - и тишина.
У меня есть более интересная задача - написать клиент к qbs session на корутинах. Но даже на мою задачу у меня нет времени/желания (я лучше прибухну и в комп поиграю), а вы хотите "готовое и на блюдечке".

Ладно, посмотрим как получить хендл чтобы звать resume, без этого не обойтись. Лектор говорит
Цитировать
Нужно реализовать простейший тип resumable вот для этого
А нафиг оно мне надо, что-то там реализовывать? Готового нету, что ли? Мне юзать надо, а не вдаваться в нюансы Улыбающийся

Есть https://github.com/lewissbaker/cppcoro, что-то должно войти в станларт с++23. Но, понятное дело, Qt там не уважают (точнее, Qt не делает ровно ничего чтобы пихать свои наработки в стандарт - у них даже есть чел в комитете но никто не обращался к нему с пропозалом или идеей что-либо стандартизовать), так что связку придется писать самому в любом случае. Ну или подождать 5 лет когда Qt снизойдет до поддержки с++20. Ну как QList::toStdVector только посложнее.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #40 : Октябрь 05, 2020, 23:59 »

Примеров использования корутин масса, некоторые он перечислил - генераторы, бесконечные последовательности, ленивые вычисления.
Всю жизнь делали примерно так (псевдокод)
Код
C++ (Qt)
value = FindFirst(&findData);
while (!value.IsEmpty()) {
 ProcessValue(value);
 value = FindNext(&findData);
}
 
Да, надо таскать с собой "контекст" (findData), ну или делать его членом класса. Это доставляет известные неудобства, но от этого еще никто не умирал. Корутина позволяет держать этот контекст в локальных переменных на стеке самой корутины. Интересная возможность, но я бы ее не переоценивал. Напр с multithreading видимо будут неприятности, стек-то не "размножить" по числу ниток (а вот findData пожалуйста). Зачем делать генератор или lazy на корутинах? Какой "выйгрышь"? Не вижу. Ну так, для понту разве что. И членом класса корутина видимо быть не могет (неприятно). И переключение стека может оказаться совсем не безобидно для пресловутой производительности.

Вместо лапши хендлеров эвентов, коллбэков или сигнал-слотов получаем простую последовательность действий как если бы мы писали синхронно. Но ожиданий и блокировок нет - код максимально производительный.
Не связано это никак с производительностью. Да, бывают случаи когда "дробление" кода сильно достает (пример с драгом), но не так уж часто. Можете привести пример без событийного цикла где корутина "в масть"? Вот я затрудняюсь.

Ну ладно, так как же оформлять корутину? Вот ссылка не хуже остальных что я видел. Пишут
Цитировать
resumable foo(){
  std::cout << "Hello" << std::endl;
  co_await std::experimental::suspend_always();
  std::cout << "Coroutine" << std::endl;
}
Опять надо рисовать какой-то resumable. Не смертельно, надо так надо, но откуда вытекает такая необходимость? Тем более для либы где с десяток вариантов замены цикла for (для эстетов). А тут ни хрена нету, чего это ?

Edit:
Напр с multithreading видимо будут неприятности, стек-то не "размножить" по числу ниток (а вот findData пожалуйста).
А похоже он сам это делает, у каждой нитки свой стек, значит каждая сохранять будет свой фрейм. Т.е. при возвращении в корутину для одной нитки - одни локальные, для другой - другие. Выносит моск Улыбающийся
« Последнее редактирование: Октябрь 06, 2020, 05:34 от Igors » Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #41 : Октябрь 06, 2020, 12:21 »

Всю жизнь делали примерно так (псевдокод)
Этот код все такой же синхронный - пока вы крутите этот луп вы не можете заниматься другими делами (не привлекая треды). Вообще файловое ИО пример отвратительный, так как в том же линуксе нет неблокирующего файлового ака completion port - вам надо самому городить огород на тредах чтобы их реализовать.
Сокеты - другое дело - пока вы "ждете" нового чанка данных, вы можете "ждать" чанк на другом сокете или новых клиентов. И вам не нужны треды и все проблемы связанные с ними - вы пишите такой же последовательный код как при блокирующем ИО но он магически становится неблокирующим за счет простого использования co_await (await в питоне)

Напр с multithreading видимо будут неприятности, стек-то не "размножить" по числу ниток (а вот findData пожалуйста).
На cppreference пример с resume() в другом треде, на хабре пример с консьюмером\продьюсером (правда у него там рейс о чем я ему сообщил в комментариях, там же более (не вглядывался) правильный вариант).
Но это всё обычно не нужно! Треды придумали не от хорошей жизни а от того что корутин не было.
Треды - вытесняющая многозадачность (ОС решает за вас когда переключать контекст). Корутины - кооперативна многозадачность, когда переключать контекст решаете вы сами. Как минимум плюс в том что контекст можно переключать реже. Например, я в Яндексе написал качалку на питоне (когда asyn/await еще не было) на "зеленых" тредах - 10к файлов качала за полчаса, тупая реализация "в цикле" без тредов имела ETA около недели. То есть без системных потоков можно получить тот же результат что и с ними, только не надо ничего синхронизовывать. Профит! Конечно, оба подхода можно комбинировать.

Зачем делать генератор или lazy на корутинах? Какой "выйгрышь"? Не вижу. Ну так, для понту разве что.
Примерно такой же как между функтором и лябмдой - писать меньше. Да, вы можете написать итератор который хранит std::function, сами делать сохранение контекста и переход к следующей "итерации"... А можно просто написать yeild в цикле.

И членом класса корутина видимо быть не могет (неприятно). И переключение стека может оказаться совсем не безобидно для пресловутой производительности.
Кто вам сказал? Обычная функция, просто return type специфический.
Переключение контекста в тредах больше + нужна синхронизация, а тут не нужно.

Не связано это никак с производительностью. Да, бывают случаи когда "дробление" кода сильно достает (пример с драгом), но не так уж часто. Можете привести пример без событийного цикла где корутина "в масть"? Вот я затрудняюсь.
Вы можете потратить больше времени на оптимизацию, так как оно освободилось потому что не надо писать лапшу коллбеков. Пайплайн лучше видно.

Ну ладно, так как же оформлять корутину? Вот ссылка не хуже остальных что я видел. Пишут
Цитировать
resumable foo(){
  std::cout << "Hello" << std::endl;
  co_await std::experimental::suspend_always();
  std::cout << "Coroutine" << std::endl;
}
Опять надо рисовать какой-то resumable. Не смертельно, надо так надо, но откуда вытекает такая необходимость? Тем более для либы где с десяток вариантов замены цикла for (для эстетов). А тут ни хрена нету, чего это ?

Ну не успели стандартизовать библиотеку для корутин. Скажите спасибо что основную машинерию дали и теперь люди могут играться с ними - глядишь новые юзкейзы (которых нет в cppcoro) откроют.
Простейшие случаи (generator, lazy) будут в следующем стандарте, сейчас можно юзать cppcoro и не париться - возвращаете generator, пишите цикл с co_yeild и вуаля.
В остальном необходимость писать resumable проистекает из расширяемости и настраиваемости. В питоне вы не можете написать lazy (там вообще корутины не расширяемые), а на плюсах - можете. async/await ждали десяток лет, они работают только с эветлупом. Продьюсер-консьюмер на тредах с корутинами вы не напишите в питоне, а на плюсах - запросто. И тд...
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #42 : Октябрь 06, 2020, 12:38 »

Но это всё обычно не нужно! Треды придумали не от хорошей жизни а от того что корутин не было.
Ну перестаньте. Улыбающийся
Треды и корутины перепендикулярные понятия. Улыбающийся
Вытесняющая многозадачность появилась в семидесятых, а "зелеными" тредами можно было пользоваться всегда.
Те кому были нужны или интересны корутины пользовались ими уже много лет. То что их добавили в стандарт C++20 не делает из них чего-то чудесного. Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #43 : Октябрь 06, 2020, 18:14 »

Треды придумали не от хорошей жизни а от того что корутин не было.
Треды - вытесняющая многозадачность (ОС решает за вас когда переключать контекст). Корутины - кооперативна многозадачность, когда переключать контекст решаете вы сами.
"Треды" позволяют задействовать все ядра, а корутины сами по себе к этому никакого отношения не имеют, это просто (лихой) способ передачи упр-я, но в рамках одной нитки. Чисто на корутинах ничего не ускорить, ничего не "разпоточить".

И вот важный момент. В примере с драгом корутина смотрится гораздо выгоднее чем вторичный eventLoop с помощью processEvents. НО основные проблемы (которые жрут 95% времени) все те же. В любом случае упр-е отдается "в неизвестность", и что там произойдет - хз. Напр замочили структуры куда корутина пишет. Часто это охраняют модальным диалогом (товарищ в этой теме упоминал). Хотя какая тогда это многозадачность?
Цитировать
-Папа, папа, а что такое многозадачность в Вындоуз?
- Подожди, сынок, вот дискета отформатится и покажу
Или замыкать фоновую задачу так чтобы она ни с кем не конфликтовала. Но головняк остается - и капитальный. Напр вот баг который я так до конца и не исправил:

- Есть окно, отображает содержимое загруженных данных в виде списка. Юзер решает их перегрузить данные из файла. Ладно, грузим файл, показываем модальный прогресс. И вот в процессе загрузки прорывается событие рисования окна (хотя setUpdatesEnabled = false). Конечно оно лезет к "недогруженным" данным и вылетает. И что делать? "Надо было загрузить данные в др место, а по окончании загрузки все заменить". Так это может оказаться нереальным, да и трудоемкость такого решения очевидна.

Поэтому я бы все-таки корутины не переоценмвал. И, простите мою навязчивость, но все же
Можете привести пример без событийного цикла где корутина "в масть"? Вот я затрудняюсь.

Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #44 : Октябрь 06, 2020, 18:24 »

Поэтому я бы все-таки корутины не переоценмвал. И, простите мою навязчивость, но все же
Можете привести пример без событийного цикла где корутина "в масть"? Вот я затрудняюсь.



Уже приводил 2 раза - генератор, lazy, продьюсер-консьюмер.
Записан
Страниц: 1 2 [3] 4   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.228 секунд. Запросов: 22.