Pull to refresh
22
0.1
Алексей @AlexPublic

User

Send message
Неслабые такие требования у системки… Т.е. я понимаю когда требуется много памяти под большую нагрузку. Но что бы даже не запускалось при двух гигах…

Интересно на чём оно такое написано.
Всё равно непонятно что за абстрагирование такое, если пул потоков и так уже выделен в отдельную абстракцию. В общем ни одного примера показывающего хотя бы минимальную полезность нескольких очередей (кроме случая с main ui, который является особым) я так не увидел.

Ну и соответственно если остановится на том, что нам не требуется много очередей, то по сути окажется что практически всё уже реализована в стандартной библиотеке.

Не реализованным остался только механизм обработки результатов выполнения дополнительных потоков в основном потоке (UI). В смысле в стандарте C++11 этого нет. В C++14 уже планируют добавить, в Boost'e уже сейчас есть. Ну и естественно можно плодить множество своих велосипедиков, как во всех этих последних статьях.
Вы похоже не понимаете что я спрашиваю. Я не спрашиваю зачем нам многопоточность или зачем нужны пулы потоков в принципе. Мне интересно почему мы не можем написать просто так (в вашей терминологии):
async_queue.async([=]{
    file_data = get_data_from_file( file_name );
    async_queue.async([=]{
        parsed_data = parse( file_data );
        main_queue.async([=]{
            update_ui_with_new_data( parsed_data ) ;
        });
    });
});

или вообще так:
async_queue.async([=]{
    file_data = get_data_from_file( file_name );
    parsed_data = parse( file_data );
    main_queue.async([=]{
        update_ui_with_new_data( parsed_data ) ;
    });
});

Т.е. зачем более одной очереди (отправку исполнения в UI поток рассматриваем как отдельный особый случай).

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

Да, и у вас совсем не легковесные потоки (которые coroutine или fiber), а именно пул потоков с очередями задач, Т.к. для легковесных потоков вам потребовался бы ещё какой-то механизм многозадачности (кооперативной как минимум, т.е. что-то типа yield).
Нууу хорошо, пусть один пул с несколькими очередями (хотя по сути это… ) в него. Всё равно не понятно зачем их несколько. Можно какие-то практические примеры? )

P.S. Как вы понимаете у меня вопросы/претензии не к вашему велосипедику, а к изначальной схеме…
SRP — жеж. Ну и отсутствие возможности для dead-lock'а — если вся работа с одним ресурсом (файловой системой, к примеру) идет через одну очередь.

Согласно SRP нам нужна отдельная сущность «пул потоков». Зачем нам надо много пулов потоков я пока так и не понял. Насчёт dead-lock'a… Во-первых это не так просто, т.к. ресурс у пула потоков наверняка не один (как минимум по числу аппаратных потоков). Ну и если всё же есть его вероятность (т.е. имеем много спящих потоков), то зачем тогда вообще использовать пул, а не просто отдельные потоки на каждую задачу?
std::async — это тот еще костыль ИМХО. Получение значения оттуда через std::future полностью блокирует текущий поток (что для Main Thread не приемлемо в принципе). А я не хочу блокироваться, я хочу получить уведомление о том, что все закончилось в контексте текущей очереди тогда, когда данные будут получены.

Ничего подобного. При использование std::async совершенно не обязательно блокировать вызывающий поток. Блокирование — это всего лишь один из сценариев. Можно и опрашивать, а можно и ждать уведомления. Кстати, как раз в той статье, на которую вы поместили ссылку в начале, были явно показаны все 3 варианта.
Удобно, чтобы для каждой из этих операций была своя очередь.

А в чём именно удобство? Мне это совершенно не очевидно.

Очевидное решение — реализация шаблона пулл потоков.

Нуу например std::async в реализации MS именно так и работает.
ГМО нормально, если это разработка каких-то наших институтов, а не от печально известной Монсанто. Если уж кто и стоит в этом мире звания «копирастов», то как раз эти товарищи. )))
А где вариант «у меня нет смартфона потому что он мне не нужен»? Мне кажется это намного более вероятный вариант чем «не знаю что это такое» или «слишком сложный».
Вот лучше бы они добавили в C++14 полноценные сопроцедуры! Они мощнее — позволяют реализовать и любой await и ещё много всего другого интересного.
По поводу cast'а… В данном случае мы передаём свои данных (в виде указателя) через очередь сообщений ОС — типизацию при этом сохранить невозможно в принципе.

Да, и кстати, все наши красивые и формальные ООП библиотеки внутри работают именно через подобные cast'ы, потому что иначе работать с функциями ОС невозможно.
А где там запуск новых потоков? ) Вложенность же от них идёт…

Ну и в любом случае, а если нам if или вообще for понадобится? )

Да и вообще, я же ещё в самом самом начале статьи написал что в принципе у нас давно есть полный набор инструментов для написания асинхронного кода. Т.е. формально мы можем записать что угодно (и даже без then ещё) давным давно. Так что вопрос остаётся только в простоте и удобстве кода — по сути синтаксическом сахаре.
Первый раз вижу эту ссылку, но судя по описанию в ней, это как раз самые классические сопроцедуры, причём не такие ограниченные как await/async, а больше похоже на Boost'ие. Это в смысле сценария использования. А в смысле внутренней реализации надо смотреть уже более подробное описание/исходники.
then же работает только как однократное поствыполнение. А с await у нас произвольная сложность, т.е. это просто обобщение then до полного подобия линейного кода. Вот смотрите пример:
void Handler(const URL url) async_code
(
    log_window+="Downloading "+url;
    auto xml=ParseXML(await_async([&]{return Download(url);}));
    log_window+="Downloading "+xml.GetValue("/update/text/url ");
    result_window+=await_async([&]{return Download(xml.GetValue("/update/text/url "));});
)

В нём такая последовательность исполнения:
1. «log_window+= в UI потоке и потом возврат из функции Handler
2. Download(url) в отдельном потоке
3. ParseXML и log_window+= снова в UI потоке
4. Download(xml.GetValue(»/update/text/url ")) в отдельном потоке
5. result_window+= снова в UI потоке.

Теперь представьте себе как некрасиво выглядел бы подобный код на вложенных then'ах.
Сделал небольшое дополнение к статье. )))
Ээээ, async_code — это не функции, а блоки асинхронного кода. Аналогом этого является всё тело async функции (причём самой первой по стеку вызова) в C# варианте. Возвращать из них какое-то значение (сразу) не имеет никакого смысла, т.к. ещё ничего не вычислено. Поэтому в моей реализации никаких возвратов нет, хотя реализация сопроцедур в Boost и поддерживает такое. Если же речь идёт о том, что бы возвращать значение после отработки всего асинхронного кода, то это просто означает что и предыдущая по стеку вызовов функция должна быть асинхронной и соответственно надо просто переставить asyn_code в неё.
Boost реализация сопроцедур корректно перекидывает исключение в родительский стек: www.boost.org/doc/libs/1_54_0/libs/coroutine/doc/html/coroutine/coroutine.html#coroutine.coroutine.exceptions_in__emphasis_coroutine_function__emphasis_
Промахнулся с ответом — он ниже ушёл. )
Ну так всё правильно. Await/async — это по сути реализация сопроцедур и здесь мы видим описание кооперативной многозадачности. Теоретически через них можно было бы даже попробовать порешать классические задачи сопроцедур… Но с учётом их не особо эффективной реализации в C# наверное всё же нет смысла.
Ну, в том виде, в котором вы привели код, оно вовсе не будет компилировать.

Ай, не то скопировал… Да и в статье тоже самое… Ужас какой. )))

Если у входного потока был установлен SyncronizationContext, или в параметрах Task.Run был передан вот такой аргумент TaskScheduler.FromCurrentSynchronizationContext(), то по окончании исполнения CalсSomething исполнение неявного коллбека с ProcessResult будет добавлено в очередь к этому SyncronizationContext-у, и исполнено на входном потоке.
Если нет — исполнение ProcessResult будет выполнено на первом попавшемся треде, не обязательно UI.
Так вот, у WinForms у входного потока SyncronizationContext есть, поэтому ProcessResult отсылается исполняться на UI.

Я в курсе) И я как раз поэтому везде и указывал что Handler вызываем из UI потока — тогда он точно туда и вернётся (если не укажем иного, а оно нам и не надо). При вызове же из не UI потока ситуация уже естественно совсем другая, но в таком случае на мой взгляд уже и вообще вся эта схема не требуется, т.к. в потоках без цикла сообщений мы свободны использовать более просты и удобные методы (см. самое начало статье).
Эх, журнал «Юный техник»… Как много родного в этой фразе…

По теме: а ведь полезная штучка для дома вышла, а не просто фан!

Information

Rating
2,538-th
Location
Москва, Москва и Московская обл., Россия
Registered
Activity