Generalnie zakładam, że strumienie nie są zsynchronizowane, to do użytkownika należy odpowiednie zablokowanie. Czy jednak takie rzeczy jak cout
biblioteka standardowa są traktowane w specjalny sposób?
Oznacza to, że jeśli wiele wątków pisze do, cout
czy mogą uszkodzić cout
obiekt? Rozumiem, że nawet po zsynchronizowaniu nadal otrzymujesz wyjście z przeplotem losowo, ale czy to przeplot jest gwarantowane. To znaczy, czy można bezpiecznie używać cout
z wielu wątków?
Czy ten dostawca jest zależny? Co robi GCC?
Ważne : jeśli powiesz „tak”, podaj jakieś odniesienie do odpowiedzi, ponieważ potrzebuję jakiegoś dowodu na to.
Nie martwię się również o podstawowe wywołania systemowe, te są w porządku, ale strumienie dodają warstwę buforowania na wierzchu.
printf
świeci, gdy cały wynik jest zapisywanystdout
w jednym ujęciu; podczas korzystania zstd::cout
każdego łącza łańcucha wyrażeń zostanie wyprowadzony osobnostdout
; Pomiędzy nimi może znajdować się inny wątek, wstdout
wyniku którego kolejność końcowego wyjścia zostanie zepsuta.Odpowiedzi:
Standard C ++ 03 nic o tym nie mówi. Gdy nie masz gwarancji, że coś jest bezpieczne dla wątków, powinieneś traktować to jako niechronione wątkowo.
Szczególnie interesujący jest tutaj fakt, że
cout
jest buforowany. Nawet jeśli wywołaniawrite
(lub cokolwiek to jest, co powoduje ten efekt w tej konkretnej implementacji) są wzajemnie wykluczające się, bufor może być współużytkowany przez różne wątki. To szybko doprowadzi do zepsucia wewnętrznego stanu strumienia.A nawet jeśli gwarantuje się, że dostęp do bufora będzie bezpieczny dla wątków, jak myślisz, co się stanie w tym kodzie?
Prawdopodobnie chcesz, aby każda linia tutaj działała we wzajemnym wykluczeniu. Ale jak wdrożenie może to zagwarantować?
W C ++ 11 mamy pewne gwarancje. FDIS mówi, co następuje w §27.4.1 [iostream.objects.overview]:
Nie otrzymasz więc uszkodzonych strumieni, ale nadal musisz zsynchronizować je ręcznie, jeśli nie chcesz, aby dane wyjściowe były śmieciami.
źródło
cout.sync_with_stdio()
jest prawdą, użyciecout
do wyprowadzania znaków z wielu wątków bez dodatkowej synchronizacji jest dobrze zdefiniowane, ale tylko na poziomie pojedynczych bajtów. W ten sposóbcout << "ab";
icout << "cd"
wykonywane w różnych wątkach mogąacdb
na przykład wyprowadzać , ale nie mogą powodować niezdefiniowanego zachowania.To świetne pytanie.
Po pierwsze, w C ++ 98 / C ++ 03 nie ma pojęcia „wątku”. Więc w tym świecie pytanie jest bez znaczenia.
A co z C ++ 0x? Zobacz odpowiedź Martinho (co, przyznaję, mnie zaskoczyło).
A co z konkretnymi implementacjami sprzed C ++ 0x? Na przykład, oto kod źródłowy
basic_streambuf<...>:sputc
z GCC 4.5.2 (nagłówek „streambuf”):Oczywiście nie powoduje to blokowania. I też nie
xsputn
. I to jest zdecydowanie rodzaj streambuf, którego używa cout.O ile wiem, libstdc ++ nie blokuje żadnych operacji na strumieniu. I nie spodziewałbym się żadnego, ponieważ byłoby to powolne.
Tak więc przy tej implementacji jest oczywiście możliwe, że wyjście dwóch wątków może się wzajemnie uszkodzić (a nie tylko przeplatać).
Czy ten kod może uszkodzić samą strukturę danych? Odpowiedź zależy od możliwych interakcji tych funkcji; np. co się stanie, jeśli jeden wątek spróbuje opróżnić bufor, podczas gdy inny spróbuje wywołać
xsputn
lub cokolwiek. Może to zależeć od tego, jak kompilator i procesor zdecydują się zmienić kolejność ładowania i przechowywania pamięci; wymagałoby dokładnej analizy, aby mieć pewność. Zależy to również od tego, co robi twój procesor, jeśli dwa wątki próbują modyfikować tę samą lokalizację jednocześnie.Innymi słowy, nawet jeśli zdarza się, że działa dobrze w obecnym środowisku, może się zepsuć podczas aktualizacji dowolnego środowiska wykonawczego, kompilatora lub procesora.
Streszczenie: „Nie chciałbym”. Zbuduj klasę rejestrowania, która wykonuje odpowiednie blokowanie lub przejdź do C ++ 0x.
Jako słabą alternatywę możesz ustawić cout na niebuforowany. Jest prawdopodobne (choć nie jest to gwarantowane), że pominie całą logikę związaną z buforem i wywoła
write
bezpośrednio. Chociaż może to być zbyt powolne.źródło
cout
.www.techrepublic.com/article/use-stl-streams-for-easy-c-plus-plus-thread-safe-logging
a także: czy standardowe strumienie wyjściowe w C ++ są bezpieczne wątkowo (cout, cerr, clog)?
AKTUALIZACJA
Proszę spojrzeć na odpowiedź @Martinho Fernandes, aby dowiedzieć się, co mówi o tym nowy standard C ++ 11.
źródło
Jak wspominają inne odpowiedzi, jest to zdecydowanie specyficzne dla dostawcy, ponieważ standard C ++ nie wspomina o wątkowaniu (to zmienia się w C ++ 0x).
GCC nie składa wielu obietnic dotyczących bezpieczeństwa wątków i operacji we / wy. Ale dokumentacja tego, co obiecuje, jest tutaj:
kluczowa sprawa to prawdopodobnie:
Nie wiem, czy cokolwiek się zmieniło od wspomnianych ram czasowych 3.0.
Dokumentację MSVC dotyczącą bezpieczeństwa wątków dla
iostreams
można znaleźć tutaj: http://msdn.microsoft.com/en-us/library/c9ceah3b.aspx :Należy pamiętać, że te informacje dotyczą najnowszej wersji MSVC (obecnie dla VS 2010 / MSVC 10 /
cl.exe
16.x). Możesz wybrać informacje dla starszych wersji MSVC za pomocą rozwijanej kontrolki na stronie (a informacje są inne dla starszych wersji).źródło