LINUX.ORG.RU

std::async deprecated (на самом деле нет)

 


3

6

Проблема в том, что std::async не справляется (и фундаментально не может справляться) со своей задачей — предоставить task-based параллелизм. Подробнее см. [1]

Если верить Eli Bendersky [2], из-за указанных в [1] проблем (и не только) std::async почти пометили бумажкой «deprecated» в 17-м стандарте, но в последний момент решили подставить кастыль в виде [3], только ради того, чтобы сохранить лицо комитета стандартизации: шутка ли — в 11-м году принять фичу в стандарт, а уже в следующей редакции (14-я была минорной, не забывайте) пометить deprecated. Это поставит под сомнение адекватность и легитимность комитета.

Вот так всегда в C++: впопыхах напихали нерабочих фич в стандарт — потом подпирают кастылями.

[1] https://bartoszmilewski.com/2011/10/10/async-tasks-in-c11-not-quite-there-yet/
[2] http://eli.thegreenplace.net/2016/the-promises-and-challenges-of-stdasync-tas...
[3] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3773.pdf

P.S. Лучше бы его и правда пометили deprecated.

★★★

Последнее исправление: utf8nowhere (всего исправлений: 6)

А что, кто-то до сих пор сомневается в полной неадекватности комитета?

mix_mix ★★★★★
()

впопыхах

Годами «пилят», якобы. А потом вот так ерунду принимают. Там же не студенты, а высокооплачиваемые спецы.

gag ★★★★★
()
Ответ на: комментарий от gag

а высокооплачиваемые спецы

А кто именно им за работу в комитете платит? Там, вроде, всё на чистом энтузиазме, люди просто в свободное время пописывают какие-то бумажки.

Gvidon ★★★★
()
Ответ на: комментарий от anonymous

У C++ есть одно неоспоримое преимущество по сравнению со ржавчиной: он нужен.

Gvidon ★★★★
()
Ответ на: комментарий от Gvidon

Там, вроде, всё на чистом энтузиазме, люди просто в свободное время пописывают какие-то бумажки.

Более того, вроде как те, кто индивидуально в комитете состоит, еще и сами платят за это «удовольствие» (по слухам что-то порядка $1200 в год).

eao197 ★★★★★
()
Ответ на: комментарий от eao197

сами платят за это «удовольствие»

Вот ведь вражины. Да еще и садюги. Придумают хрень, потом пиши на ней. Хвала Заратустре мы потихоньку на D переходим с явы и плюсов.

anonymous
()
Ответ на: комментарий от anonymous

Хвала Заратустре мы потихоньку на D переходим с явы и плюсов.

С большой долей вероятности, вы не умели толком ни того, ни другого. Ну а т.к. D не сильно проще плюсов, то можно ожидать через какое-то время стонов и по поводу D.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Умели умели. А Ди не то, что сильно проще, он сильно лучше и продуманнее. И писать на нем сильно приятнее, да, чем на недоразумении из 80х, которое закопать надо было еще лет 20 назад.

Зато теперь у нас везде все одинаково. И во внутренних сервисах, и на дестктопах у клиентов, и в вебе. Много, конечно старого говна-легаси осталось, но с Ди это на удивление быстро решается. Как-то уже даже с++ трогать даже десятиметровой палкой не хочется.

anonymous
()
Ответ на: комментарий от anonymous

Умели умели.

Обычно те, кто умеют, заняты решением прикладных вопросов, а не борьбой с языком или его инфраструктурой. Причем это к любому языку относится, будь то C++, Java, C#, Scala или Haskell.

Другое дело, если вы неподходящий для своей задачи язык выбрали. Скажем, взяли C++ и Java там, где можно было делать все на C#. Ну тут уж вы сами себе...

eao197 ★★★★★
()

Вот так всегда в C++: впопыхах напихали

Ну да, ну да, первый стандарт - в 1998-м, следующий - в 2011-м. Впопыхах конечно)

asaw ★★★★★
()

Вот интересно, кстати говоря: а кто-то обещал этому самому Бартошу Милевски, что std::async — это прям таки инструмент для поддержки task-based параллелизма в C++?

По-моему, озвученные Бартошем претензии к std::async — это как претензии к std::string, что там нет поддержки Unicode и преобразования между кодировками, инструментов вроде tolower, toupper, алгоритмов starts_with/end_with и пр.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Обычно те, кто умеют, заняты решением прикладных вопросов

Чушь говоришь. Старое легаси копится годами и однажды просто проще все продумать и нарисовать новые схемы, чем бороться с гидрой и подпирать постоянно костылями. Времена меняются. Когда-то не могли подумать про «облачные вычисления», про распределенку и прочее. Сейчас это нужно и дает профиты. Есессно не сразу взяли и резко начали переходить. процесс долгий был, с обсуждениями, тестами, пробами и только потом плавный переход и выпиливание старого говна. Это и была одна из большИх задач.

Скажем, взяли C++ и Java там, где можно было делать все на C#.

типичный аналитег ЛОРа. Них не знает, но мнение имеет. Не было никакого сишарпа, когда все начиналось. Зато был ынтерпрайз, который уверовал в яву (1.2 тогда была ... брр) ну и Си и кресты к которым как раз стандарт подоспел, как меинстрим.

anonymous
()
Ответ на: комментарий от anonymous

Старое легаси копится годами

Ну, если копиться, тогда да. Тогда приходят анонимные всемогутеры и говорят, что если взять и переписать на новом модном языке, то все прям взлетит.

Не было никакого сишарпа, когда все начиналось. Зато был ынтерпрайз, который уверовал в яву (1.2 тогда была ... брр) ну и Си и кресты к которым как раз стандарт подоспел, как меинстрим.

Ну тут возникает пару вопросов:

1. Что вы делали все это время? Просто месили свое древнее говно, пока где-то рядом развивался не только C++, но и Java? Эволюция используемых вами инструментов прошла мимо вас?

2. Сколько же у вас кода накопилось, что сейчас у вас есть время и ресурсы на big rewritting? По 10KLOC на разработчика? Или по 15KLOC?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Вот интересно, кстати говоря: а кто-то обещал этому самому Бартошу Милевски, что std::async — это прям таки инструмент для поддержки task-based параллелизма в C++?

My expectation was that C++11 “tasks” that are created using std::async should be abstracted from threads, just as they are in task-based parallelism.

My expectation

Он сам себе это придумал.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Он сам себе это придумал.

Ну может были какие-то разговоры об этом, когда std::async в стандарт принимали.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Возможно это даже и не претензии, а просто обзор того, чего можно делать с std::async, а чего - нет:

Conclusion and practical advice

This article started by expounding the virtues of std::async compared to plain std::threads, but finished by pointing out numerous problems with std::async that one needs to be aware of. So, what do we do?

I actually think that by being careful to stay within the well-defined limits of std::async, we can enjoy its benefits without running into the gotchas. Specifically:

Prefer std::async to std::thread. Futures are just too useful to ignore; especially if your code deals with exception handling, this is the only sane way to stay safe. Results provided by different threads should be wrapped in futures. Always use the std::launch::async policy with std::async if you actually want multi-threading. Do not rely on the default policy. Do not use deferred unless you have very special needs. Remember that deferred is just syntactic sugar over holding a function pointer to call it later.

If you need a real thread pool or some other higher-level concurrency construct, use a library or roll your own. Standard objects like std::future, std::promise and std::packaged_task can be very helpful.

asaw ★★★★★
()
Ответ на: комментарий от eao197

Тогда приходят анонимные всемогутеры и говорят,

нет, конечно. Приходят всемогутеры регистранты и предлагают пилить дальше костыли на морально высохшем говне.

Написал тут длинный текст, по подумал, что фанатику с маркетоидными замашками что-то доказывать — себе дороже. Когда будешь работать в команде, которая на 3 континентах расположена мож поймешь. А может и нет.

anonymous
()
Ответ на: комментарий от asaw

Мне показалось, что этот пост один из постов вида «я думал оно вот какое, а на самом-то деле...». Много текста дабы сделать вывод, очевидный после прочтения описания std::async на том же cppreference.com.

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

Когда будешь работать в команде, которая на 3 континентах расположена мож поймешь.

Да уж, круче вас только горы и яйца. Раз уж вы настолько круты, чтож вы на простые вопросы не можете ответа дать?

А то ведь складывается впечатление, что у вас там никакого процесса разработки нет, поддержкой старого кода занимаетесь «на отвали», обновлением кодовой базы (с C++98/03 до, хотя бы, C++14 и с Java 1.4 до Java 7) никто не озадачивался. Зато в волшебную пилюлю «перепишем на D» поверили как маленькие.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Согласен, букв многовато. Но и замечание правильное: default policy непонятно для чего нужна. Она просится для cooperative multitasking, но cooperative multitasking пока что нет.

asaw ★★★★★
()
Ответ на: комментарий от eao197

обновлением кодовой базы (с C++98/03 до, хотя бы, C++14 и с Java 1.4 до Java 7)

facepalm

Ты реально такой дурной? Где написано, что не озадачивались?

Зато в волшебную пилюлю «перепишем на D» поверили как маленькие.

doublefacepalm

Да, нужно было верить в волшебную пилюлю «давайте все переведем на С++14 и все станет заебись». И еще раз перечитай, если умеешь в русские буквы, переход не был сиюминутным. Это было долго, с выпиливанием, разделением и обсуждениями. Хотя кому я это объясняю ...

А то ведь складывается впечатление

Складывается впечатление, что ты сам ничего сложнее хеловорда не писал и работал в команде, которая сидит в одном офисе и пишет только на одном языке и один и тот же «екзешник» в течении лет 10.

anonymous
()
Ответ на: комментарий от asaw

Имхо, default policy хороша в ситуации, когда в приложение объединяется множество независимых друг от друга кусков, в каждом из которых применяется std::async. Попробую на пальцах пояснить...

Как по мне, так std::async хороша для каких-то простых и мелких вещей. Вроде того, что нам нужно прочитать содержимое файла, попутно подготовив набор регулярок, которым мы хотим по файлу пройтись. С помощью std::async это делается очень просто:

some_result process_file(const char * file_name) {
  auto content = std::async( load_content, file_name );
  auto regexes = prepare_regex(...);
  return regexes.process( content.get() );
}
При этом автору process_file не суть важно, будет ли загрузка содержимого файла выполняться в другом потоке, или же в том же (при вызове content.get()). Он просто дал возможность для распараллеливания своих действий и все.

Когда таких функций в итоге окажется 100500, то default policy может оказаться более вменяемым подходом, чем явное указание std::launch::async. Ведь тогда runtime может что-то запускать асинхронно, что-то синхронно.

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

Где написано, что не озадачивались?

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

Да, нужно было верить в волшебную пилюлю «давайте все переведем на С++14 и все станет заебись».

Так вы хоть пробовали или нет? Вот у меня есть опыт работы с кодом, который плавно эволюционировал из C++03 в C++14. И есть опыт работы с кодом, который застыл в C++03 из-за проблем с процессом разработки. Разница значительная. Драматическая, я бы сказал.

Это было долго, с выпиливанием, разделением и обсуждениями. Хотя кому я это объясняю

Вы не объясняете ничего. Просто предлагаете поверить вам на слово. Только вот с чего бы? С того, что вы умеете надувать щеки по поводу территориально разнесенной команды?

Как вы в этих условиях собираетесь должный уровень знаний D поддерживать, к примеру?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

вы на них ответить не можете.

Сам придумал?

Вот у меня есть опыт работы с кодом, который плавно эволюционировал из C++03 в C++14.

Просто предлагаете поверить вам на слово.

Просто предлагаете поверить вам на слово.

Нет, что вы зачем? яж такое же трепло, как и вы.

anonymous
()
Ответ на: комментарий от anonymous

Сам придумал?

Ну так ткните пальцем в место, где вы ответили на заданные вам вопросы.

Просто предлагаете поверить вам на слово.

Да я-то могу привести примеры, где C++11/14 позволяет писать меньше, эффективнее и безопаснее, чем C++03. Вы вот про свои мегаразработки ничего сказать не можете, что характерно.

eao197 ★★★★★
()
Ответ на: комментарий от utf8nowhere

Та же субстанция, что и C++, просто с другим привкусом rust vs. c++: hash & allocators

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от anonymous

пилить дальше костыли на морально высохшем говне.
Написал тут длинный текст, по подумал, что фанатику с маркетоидными замашками что-то доказывать — себе дороже

Ну да, ты со своими «аргументами» выглядишь куда убедительнее.

О D послушать интересно, так что если есть, что рассказать кроме эмоций, то вперёд.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Про использование D у себя в ЖЖ интересно пишет Дмитрий Попов (он же thedeemon). Правда, у него условия для D вполне себе хорошие — минимум платформ, которые нужно поддерживать, разработка в одиночку, задачи-числодробилки.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

return regexes.process( content.get() );

И где здесь асинхронность? Это по сути ленивость, не? Читать файл-то он будет все равно тогда, когда будет вызван content.get()? В чем профит?

anonymous
()
Ответ на: комментарий от anonymous

мы потихоньку на D переходим с явы и плюсов

мы

вас много там, в палате? или это население головы одного анонимуса?

slackwarrior ★★★★★
()
Ответ на: комментарий от anonymous

Читать файл-то он будет все равно тогда, когда будет вызван content.get()?

Это зависит от реализации. Без явного указания std::launch функция std::async может как запустить задачу на какой-то другой нити, так и ничего не сделать, отложив исполнение задачи до обращения к get().

Соответственно, если run-time-библиотека достаточно умная и может предоставить отдельную нить под чтение файла, то профит будет. Если не может, то будет обычный однопоточный код.

eao197 ★★★★★
()
Последнее исправление: eao197 (всего исправлений: 1)
Ответ на: комментарий от anonymous

Это по сути ленивость, не?

Не.

Читать файл-то он будет все равно тогда, когда будет вызван content.get()?

Не обязательно. Если реализация изволит выполнить асинхронно — выполнится асинхронно.

По-хорошему надо явно приказать запустить асинхронно.

А ещё лучше не пользоваться std::async.

anonymous
()
Ответ на: комментарий от eao197

если run-time-библиотека

Что такое run-time-библиотека? У крестов же нет шедулера какого-то, оно не может асинхронно в одном потоке выполняться, только асинхронно параллельно? Или если функция запускает отдельный поток, будет профит? Но это же невероятно жирно, запускать потоки на такие задачи, не? Я думал, кресты про оптимизацию. Не разумнее ли создавать потоки сразу, а потом на них запускать задачи, вроде этой — в юникс потоки создавать очень тяжело, да и переключение много стоит.

anonymous
()
Ответ на: комментарий от eao197

Про использование D у себя в ЖЖ интересно пишет Дмитрий Попов (он же thedeemon)

Спасибо, почитаю.

DarkEld3r ★★★★★
()
Ответ на: комментарий от anonymous

Что такое run-time-библиотека?

run-time библиотека C++, которая реализует такие вещи, как std::thread, std::mutex и т.д.

У крестов же нет шедулера какого-то,

Что мешает быть какому-то пулу рабочих нитей, которые плюсовый run-time создаст при первом вызове std::async и нити из которого затем будут раздаваться std::async-ам?

оно не может асинхронно в одном потоке выполняться, только асинхронно параллельно?

Тут термин асинхронно не совсем в тему. std::async может запустить нить, на которой, в параллель, будет выполнен вызов load_content. Сам же вызов load_content будет синхронным. Но для process_file будет создаваться впечатление асинхронной работы.

Или если функция запускает отдельный поток, будет профит? Но это же невероятно жирно, запускать потоки на такие задачи, не?

Зависит от тяжести IO-операции. Кроме того, если std::async не создает потоки на каждый чих, а использует какой-то пул, то выделение уже готовой нити из пула может быть довольной дешевой операцией.

Я думал, кресты про оптимизацию.

Прежде всего они для думания головой. В плюсах можно сделать эффективно и с низкими накладными расходами. Но не факт, что это будет a) просто и b) это будет сделано с помощью средств из стандартной библиотеки.

Тот же std::async — это удобная штука для простых задач. Но заменой каким-нибудь Intel TBB или Microsoft PPL она не является.

Не разумнее ли создавать потоки сразу, а потом на них запускать задачи, вроде этой — в юникс потоки создавать очень тяжело, да и переключение много стоит.

От задачи зависит. Если разработчик контролирует в приложении все от и до, то может и разумнее. Я же специально говорил о случае, когда приложение собирается из разрозненных кусков. Вася Пупкин писал свой кусок, Ваня Федоров — свой, ничего не зная про Васю Пупкина. Затем Вова Сидоров будет это все собирать под одной крышей. И если Вася и Ваня явно прописывали у себя std::launch::async, то количество потоков в приложении может превысить пределы разумного. А вот если использовали default policy, то в худшем случае будет обычная однопоточная работа. А в лучшем, если в run-time есть пул потоков и хорошее управление этим пулом, то и выигрыш.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Что мешает быть какому-то пулу рабочих нитей

То есть это должен быть такой libstdc++? И есть реализации, которые так делают? А как этим управлять тогда, должно ли оно тредпул создавать, сколько тредов и.т.д., через эти политики? Так в стандарте только две, нет? То есть нестандартные расширения?

anonymous
()
Ответ на: комментарий от anonymous

Да вы описание почитайте, все станет понятно: http://en.cppreference.com/w/cpp/thread/async

Там оставлено место для нестандартных вещей. Через которые все может и делаться в конкретном проекте. Если же оставаться в рамках стандарта, то либо запуск отдельной нити, либо синхронное исполнение на той же нити при обращении к future::get.

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

Да. Внесли в стандарт абсолютно неюзабельную вещь.

Возьмём вектор. Им в 93% случаев просто пользуешься и тебя всё устраивает. Если вдруг нужно использовать его в каких-то жоских условиях, то тогда начинаешь тюнить. Например, пишешь свой аллокатор и суёшь вектору.

Так по-хорошему и должно быть с async. Реализация содержит какой-нибудь thread pool и запускает на нём таски асинхронно. Да, нельзя тюнить этот пул. Он работает, может, не самым оптимальным образом, но в общем случае, если ты не пишешь сервер для 10k одновременно открытых соединений, сойдёт. В специализированных случаях выкидываешь async и реализуешь свой пул.

А в реальности async-ом пользоваться вообще почти невозможно. Ситуация как с локалями. Кагбы они есть, и кагбы их нет, т.к. кроме локали «C» тебе ничего не гарантируют, даже формата названия других локалей.

Таких вещей, которые только дразнят, но не дают — в стандарте быть не должно. Это, в конце-концов, отнимает время у множества людей. От производителей стандартных библиотек, которые вынуждены реализовывать никому не нужные вещи, до пользователей, которые сначала что-то изучают и на что-то надеются, а потом вынуждены читать объяснения, почему то, что они намеревались использовать, использовать не надо.

Так же это отнимает время членов комитета. Приходится долго решать, выпиливать фичу из стандарта или, раз уж Джосаттису так нравится его кривой высер, то пусть остаётся. Нет, вы только гляньте на этот скулёж (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3780.pdf):

Why Deprecating async() is the Worst of all Options

The concurrency working group has come to the conclusion to propose to deprecate async(), which we introduced as new high-level concurrency interface with C++11. In this paper I want to point out why this is the worst of all options we had to solve the underlying “problem” so that we can avoid making this mistake.

As a disclaimer, I am not neutral in respect to this subject and a little bit emotional. While I try to present facts and opinions in a fair way, please apologize just in case this sounds offending for you. Also, please respect that English is not my native language that that we Germans tend to have no problems to make statements without only using nice words.

utf8nowhere ★★★
() автор топика
Ответ на: комментарий от utf8nowhere

Нет, вы только гляньте на этот скулёж (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3780.pdf):

Так товарищ Джосаттис там все очень толково описал (в том числе и то, что проблема не столько с самим async, сколько с разным поведением std::future). А вот вы, походу, дальше процитированного фрагмента и не прочитали.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Правда, у него условия для D вполне себе хорошие — минимум платформ, которые нужно поддерживать

Эммм, а у D проблема с поддержкой разных платформ? А то я всё как-то собираюсь к нему присмотреться...

hobbit ★★★★★
()
Ответ на: комментарий от hobbit

Ну так присмотритесь. Если нужен нативный язык с GC, то выбор все равно небольшой. Уж лучше D, чем Go :)

Основной компилятор DMD, вроде как есть для основных платформ (раньше, правда, были какие-то проблемы с его качеством вне Linux-а или вне Windows, как сейчас не знаю). А вот компиляторы GDC и LDC, которые могут генерировать код практически под что угодно, несколько отстают от DMD.

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от anonymous

кресты про оптимизацию. но оптимизация делается ручками, ручками. и башкой. а если нужно наговнять быстро и как попало - это пистон: там никакой оптимизации, зато всё готовое, моск напрягать не нужно.
создавай в своём проекте хоть конечные автоматы, хоть потоки запускай, хоть шедулер свой. ограничений на твою фантазию нет, кроме твоих навыков программирования.

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 1)
Ответ на: комментарий от eao197

проблема не столько с самим async, сколько с разным поведением std::future

«Проблема с future, который берётся из async — это не проблема async» Такой неприкрытой казуистики я ещё не встречал. Ты тоже, что ли, «not neutral in respect to this subject and a little bit emotional»? С чего?

utf8nowhere ★★★
() автор топика
Ответ на: комментарий от asaw

Ну да, ну да, первый стандарт - в 1998-м, следующий - в 2011-м. Впопыхах конечно)

)))

utf8nowhere ★★★
() автор топика
Ответ на: комментарий от utf8nowhere

Еще раз, вы перечитайте написанное Джосаттисом. Он там называет вещи своими именами. И прямо говорит, что да, в результате получилось не хорошо, что future, возвращенный из async, ведет себя иначе, чем future, полученный каким-то другим способом. Но как получилось, так и получилось. Просто взять и задеприкейтить — это худший вариант и тому Джосаттис приводит нормальные обоснования.

Т.е. люди понимают проблему и стараются найти выход.

А вот вы чего пытаетесь сказать в этой теме — не понятно. Если мосье Д'Артаньян, то может он подскажет, что нужно сделать?

eao197 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.