2014-05-04 24 views
27

Gdy próbuje odpowiedzieć na kolejną Stackoverflow question, zdałem sobie sprawę, że to proste C++ 11 urywek jest domyślnie blokuje wywołującego wątku:Dlaczego destruktor przyszłości powrócił z blokowania `std :: async`?

std::async(std::launch::async, run_async_task) 

Dla mnie byłoby to wydawało kanonicznej C++ 11 sposób, aby uruchomić zadanie asynchronicznie bez dbania o wynik. Zamiast tego trzeba wyraźnie utworzyć i odłączyć wątek (patrz answer na wymienione pytanie), aby to osiągnąć.

Oto moje pytanie: czy istnieje jakikolwiek powód, dla bezpieczeństwa/poprawności, że destruktor std::future musi być blokowany? Czy to nie wystarczy, jeśli blokuje się tylko na get, a poza tym, jeśli nie jestem zainteresowany wartością zwracaną lub wyjątkiem, to po prostu ogień i zapomnieć?

+0

Po prostu odpowiedziałeś na swój komentarz, a tutaj masz pytanie lol – texasbruce

+1

Było dużo dyskusji na temat tego zachowania. Zapoznaj się z następującymi dokumentami: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3773.pdf i http://www.open-std.org/jtc1/ sc22/wg21/docs/papers/2013/n3776.pdf – nosid

+1

@texasbruce: Tak, ponieważ to naprawdę mnie nęka. Byłem pewien, że jest to * nowy sposób C++ 11y i naprawdę byłem zaskoczony, że blokuje to, nawet jeśli nie zależy mi na wyniku (wspomniano tylko w jednej notatce na końcu). O ile nie ma takiej potrzeby, nie sądzę, żeby to był dobry interfejs. –

Odpowiedz

23

Blokowanie destruktorów kontraktów futures zwracanych przez std :: async oraz wątków: To kontrowersyjny temat. Poniższa lista dokumentów w porządku chronologicznym odzwierciedla niektóre z dyskusji przez członków komisji:

Chociaż nie było wiele dyskusji, nie ma zaplanowanych dla C++ 14 zmian dotyczących blokowanie zachowania destruktorów std :: future i std :: thread.

Jeśli chodzi o Twoje pytanie, najciekawszy artykuł jest prawdopodobnie drugi, autorstwa Hansa Boehma. Cytuję niektóre części, aby odpowiedzieć na twoje pytanie.

N3679: Async() future destructors must wait

[..] Futures zwracane przez async() z async uruchomić polityki czekać w ich destruktora dla skojarzonego wspólną stanie stać się gotowy. Zapobiega to sytuacji, w której powiązany wątek nadal działa, i nie ma już możliwości oczekiwania na zakończenie, ponieważ powiązana przyszłość została zniszczona. Bez bohaterskich wysiłków, aby w przeciwnym razie czekać na zakończenie, taki "uciekający" wątek może nadal działać poza czasem życia obiektów, od których zależy.

[Przykład]

Wynik końcowy może być przekroju nici „przebój pamięć”. Ten problem jest oczywiście unikany, jeśli zostanie nazwany [...], zanim [futures] zostaną zniszczone. Trudność [...] polega na tym, że nieoczekiwany wyjątek może spowodować ominięcie tego kodu.W związku z tym zwykle potrzebny jest pewien rodzaj osłony osłaniającej, aby zapewnić bezpieczeństwo. Jeśli programista zapomni dodać osłony, wydaje się prawdopodobne, że atakujący mógłby wygenerować np. wyjątek bad_alloc w odpowiednim momencie, aby skorzystać z niedopatrzenia i spowodować zastąpienie stosu. Możliwe jest również kontrolowanie danych używanych do nadpisywania stosu, a tym samym uzyskanie kontroli nad procesem. Jest to wystarczająco subtelny błąd, który w naszym odczuciu może zostać przeoczony w prawdziwym kodzie.

Aktualizacja: Michael Wong Trip Raport zawiera również kilka ciekawych informacji na temat wyników posiedzenia we wrześniu 2013 roku:

The View from the C++ Standard meeting September 2013 Part 2 of 2.

W kwestii tej asynchronicznych destruktorów nie powinniśmy blokować, poświęciliśmy wiele dyskusji na ten temat. [..] Jedyną pozycją, która otrzymała znaczną pomoc, było [..] udzielanie porad, że przyszłe destruktory nie będą blokować, chyba że zostaną zwrócone z asynchronizmu, czyniąc z tego znaczący wyjątek. [..] Po znaczącej dyskusji jedyną częścią, którą próbowaliśmy przeprowadzić, była N3776, próba wyjaśnienia, że ​​pozycja ~future i ~shared_future nie blokuje, chyba że w obecności asynchronicznej. Podjęto próbę wydania wycofania zgodnie z wersją C. Wycofanie asynchronizacji bez zastąpienia. Ten ruch był właściwie prawie wysunięty. Ale [..] umarł jeszcze zanim dotarł do stołu operacyjnego.

+1

Świetna, dokładnie taka odpowiedź, której szukałem! Nadal nie był przekonany, że to właściwa decyzja, ale przynajmniej kompromisy są teraz dla mnie jasne. Domyślam się, że jest to ekstremalna forma RAII, na przykład upewniając się, że w destruktorze jest zamknięta obsługa plików. –

+1

O tym, że zostało to naprawione w C++ 14, ta propozycja brzmi dla mnie tak: [n3773] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3773. pdf). Lub przynajmniej daje możliwość ręcznego "odłączenia". –

+2

Byłem pewien, że widziałem pewne informacje na temat wyniku dyskusji. Teraz znalazłem go i zaktualizowałem odpowiedź. – nosid

Powiązane problemy