Jest to luźno związane z tym pytaniem: czy std :: thread jest w puli w C ++ 11? . Chociaż pytanie jest inne, intencja jest taka sama:
Pytanie 1: Czy nadal ma sens używanie własnych (lub biblioteki innej firmy) pul wątków, aby uniknąć kosztownego tworzenia wątków?
Wniosek z drugiego pytania był taki, że nie można liczyć na std::thread
to, że zostaniemy połączeni (może lub nie). std::async(launch::async)
Wydaje się jednak, że ma znacznie większą szansę na połączenie.
Nie wydaje mi się, że jest to wymuszone przez standard, ale IMHO spodziewałbym się, że wszystkie dobre implementacje C ++ 11 będą używać puli wątków, jeśli tworzenie wątków jest powolne. Tylko na platformach, na których tworzenie nowego wątku jest niedrogie, spodziewałbym się, że zawsze tworzą nowy wątek.
Pytanie 2: Tak właśnie myślę, ale nie mam faktów, aby to udowodnić. Mogę się bardzo mylić. Czy to zgadywanie?
Na koniec przedstawiłem tutaj przykładowy kod, który najpierw pokazuje, jak myślę, że tworzenie wątków można wyrazić za pomocą async(launch::async)
:
Przykład 1:
thread t([]{ f(); });
// ...
t.join();
staje się
auto future = async(launch::async, []{ f(); });
// ...
future.wait();
Przykład 2: odpal i zapomnij wątek
thread([]{ f(); }).detach();
staje się
// a bit clumsy...
auto dummy = async(launch::async, []{ f(); });
// ... but I hope soon it can be simplified to
async(launch::async, []{ f(); });
Pytanie 3: Czy wolisz async
wersje od thread
wersji?
Reszta nie jest już częścią pytania, ale tylko dla wyjaśnienia:
Dlaczego wartość zwracana musi być przypisana do zmiennej fikcyjnej?
Niestety, obecny standard C ++ 11 wymusza przechwycenie zwracanej wartości std::async
, ponieważ w przeciwnym razie wykonywany jest destruktor, który blokuje się do momentu zakończenia akcji. Niektórzy uważają to za błąd w normie (np. Przez Herba Suttera).
Ten przykład z cppreference.com ładnie to ilustruje:
{
std::async(std::launch::async, []{ f(); });
std::async(std::launch::async, []{ g(); }); // does not run until f() completes
}
Kolejne wyjaśnienie:
Wiem, że pule wątków mogą mieć inne uzasadnione zastosowania, ale w tym pytaniu interesuje mnie tylko aspekt unikania kosztownych kosztów tworzenia wątków .
Myślę, że nadal istnieją sytuacje, w których pule wątków są bardzo przydatne, zwłaszcza jeśli potrzebujesz większej kontroli nad zasobami. Na przykład serwer może zdecydować o obsłudze tylko określonej liczby żądań jednocześnie, aby zagwarantować szybkie czasy odpowiedzi i zwiększyć przewidywalność wykorzystania pamięci. Pule wątków powinny być w porządku.
Zmienne lokalne wątku mogą być również argumentem dla twoich własnych pul wątków, ale nie jestem pewien, czy ma to zastosowanie w praktyce:
- Tworzenie nowego wątku ze
std::thread
startami bez zainicjowanych zmiennych lokalnych wątku. Może nie tego chcesz. - W wątkach utworzonych przez program
async
jest dla mnie niejasny, ponieważ wątek mógł zostać ponownie użyty. Z mojego zrozumienia nie ma gwarancji, że zmienne lokalne wątku zostaną zresetowane, ale mogę się mylić. - Z drugiej strony korzystanie z własnych pul wątków (o stałym rozmiarze) zapewnia pełną kontrolę, jeśli naprawdę tego potrzebujesz.
std::async()
. Nadal jestem ciekawy, jak obsługują nietrywialne destruktory thread_local w puli wątków.
launch::async
to traktuje ją tak, jakby była tylko launch::deferred
i nigdy nie wykonuje jej asynchronicznie - w efekcie ta wersja libstdc ++ „wybiera” zawsze używać odroczonego, chyba że wymuszono inaczej.
std::async
mógł być piękną rzeczą dla wydajności - mógł to być standardowy system wykonywania krótkich zadań, naturalnie wspierany przez pulę wątków. W tej chwili jest to tylko std::thread
z przyczepionymi bzdurami, aby funkcja wątku mogła zwrócić wartość. Aha, i dodali zbędną „odroczoną” funkcjonalność, która std::function
całkowicie pokrywa się z zadaniem .
std::async(launch::async)
Wydaje się jednak, że ma znacznie większe szanse na połączenie." Nie, uważam,std::async(launch::async | launch::deferred)
że to może być połączone. Samolaunch::async
zadanie ma zostać uruchomione w nowym wątku, niezależnie od tego, jakie inne zadania są uruchomione. Dzięki politycelaunch::async | launch::deferred
implementacja wybiera, którą politykę, ale co ważniejsze, może opóźnić wybór tej polityki. Oznacza to, że może poczekać, aż wątek w puli wątków stanie się dostępny, a następnie wybrać zasadę asynchronizacji.