Це питання пов'язане з цим питанням: Чи об'єднано std :: thread в C ++ 11? . Хоча питання відрізняється, намір однаковий:
Запитання 1: Чи все-таки має сенс використовувати власний (або сторонні бібліотеки) пули потоків, щоб уникнути створення дорогих ниток?
В іншому питанні було зроблено висновок про те, що ви не можете розраховувати на std::thread
об'єднання (це може бути, а може і не бути). Однак, std::async(launch::async)
схоже, є набагато більший шанс бути об'єднаним.
Не думаю, що це вимушене стандартом, але IMHO я би сподівався, що всі хороші C ++ 11 реалізації використовуватимуть об'єднання ниток, якщо створення потоків буде повільним. Тільки на платформах, де створити нову нитку недорого, я б сподівався, що вони завжди породять нову нитку.
Питання 2: Це саме те, що я думаю, але у мене немає фактів, які б це підтверджували. Я дуже добре помиляюся. Це освічена здогадка?
Нарешті, тут я надав зразок коду, який вперше показує, як я думаю, створення ниток можна виразити через async(launch::async)
:
Приклад 1:
thread t([]{ f(); });
// ...
t.join();
стає
auto future = async(launch::async, []{ f(); });
// ...
future.wait();
Приклад 2: Пожежа та забудь нитку
thread([]{ f(); }).detach();
стає
// a bit clumsy...
auto dummy = async(launch::async, []{ f(); });
// ... but I hope soon it can be simplified to
async(launch::async, []{ f(); });
Питання 3: Чи віддасте перевагу async
версій перед thread
версіями?
Решта вже не є частиною питання, а лише для уточнення:
Чому повернене значення має присвоюватися фіктивній змінній?
На жаль, поточні C ++ 11 стандартних сил, які ви фіксуєте повернене значення std::async
, оскільки в іншому випадку виконується деструктор, який блокує, поки дія не припиняється. Дехто вважає помилкою у стандарті (наприклад, Герб Саттер).
Цей приклад із cppreference.com чудово ілюструє це:
{
std::async(std::launch::async, []{ f(); });
std::async(std::launch::async, []{ g(); }); // does not run until f() completes
}
Ще одне уточнення:
Я знаю, що пули потоків можуть мати інші законні можливості, але в цьому питанні мене цікавить лише аспект уникнення дорогих витрат на створення потоку .
Я думаю, що все ще існують ситуації, коли пули ниток дуже корисні, особливо якщо вам потрібно більше контролювати ресурси. Наприклад, сервер може вирішити обробляти лише фіксовану кількість запитів одночасно, щоб гарантувати швидкий час відповіді та збільшити передбачуваність використання пам'яті. Нитки басейни повинні бути добре, тут.
Локальні змінні потоків також можуть бути аргументом для ваших власних пулів потоків, але я не впевнений, чи це актуально на практиці:
- Створення нового потоку із
std::thread
запуском без ініціалізованих змінних локальних змінних. Можливо, це не те, чого ти хочеш. - У нитках, породжених
async
, для мене дещо незрозуміло, тому що нитку можна було б використати повторно. З мого розуміння, локальні змінні потоку не можуть бути скинуті, але я можу помилитися. - Використання власного пулу потоків (фіксованого розміру), з іншого боку, дає вам повний контроль, якщо він вам справді потрібен.
std::async()
. Мені все ще цікаво побачити, як вони підтримують нетривіальні деструктори thread_local у пулі ниток.
launch::async
то вона трактує її як би єдину launch::deferred
і ніколи не виконує її асинхронно - так, фактично, ця версія libstdc ++ "вибирає" завжди використовувати відкладені, якщо не вимушено інше.
std::async
це могло бути прекрасною справою для продуктивності - це могла бути стандартна система виконання коротких завдань, природно підкріплена пулом потоків. Наразі, це просто std::thread
з деяким дерьмом, задіяним, щоб функція потоку змогла повернути значення. О, і вони додали зайвий "відкладений" функціонал, який повністю перекриває роботу std::function
.
std::async(launch::async)
схоже, є набагато більший шанс бути об'єднаним". Ні, я вважаю,std::async(launch::async | launch::deferred)
що це може бути об'єднано. Щойноlaunch::async
завдання потрібно запустити на нову нитку незалежно від того, які інші завдання виконуються. З політикоюlaunch::async | launch::deferred
тоді реалізація може вибрати, яку політику, але що важливіше, це затягувати з вибором політики. Тобто він може зачекати, поки нитка в пулі потоків стане доступною, а потім вибрати політику асинхронізації.