Czy mądrze jest zamienić boost :: thread i boost :: mutex na odpowiedniki c ++ 11?

153

Motywacja: powód, dla którego rozważam to, że mój genialny kierownik projektu uważa, że ​​doładowanie jest kolejną zależnością i że jest okropne, ponieważ "jesteś od niego zależny" (próbowałem wyjaśnić jakość wzmocnienia, ale po pewnym czasie zrezygnowałem :( Mniejszym powodem, dla którego chciałbym to zrobić, jest to, że chciałbym nauczyć się funkcji języka c ++ 11, ponieważ ludzie zaczną pisać w nim kod.

  1. Czy istnieje mapowanie 1: 1 między odpowiednikami #include<thread> #include<mutex>i boost?
  2. Czy rozważyłbyś dobry pomysł, aby zamienić rzeczy boost na rzeczy z c ++ 11
    . Moje użycie jest prymitywne, ale czy są przykłady, kiedy std nie oferuje tego, co zapewnia wzmocnienie? Czy (bluźnierstwo) odwrotnie?

PS Używam GCC, więc są tam nagłówki.

NoSenseEtAl
źródło
46
Wytyczne IMO Google dotyczące kodowania są głupie na wiele sposobów ... Na przykład. nie pozwalają na auto z C ++ 11 ... :)
NoSenseEtAl
5
Wskazówki dotyczące cytowania: [auto] utrudnia czytelność [ponieważ usuwa] zaznaczoną nadmiarowość (np. Nazwy typów), która może być pomocna dla czytelników.
Andrew Tomazos,
31
for (auto it = v.begin () ... :)
NoSenseEtAl
15
@ AndrewTomazos-Fathomling: Naprawdę? Osobiście nie sądzę, żebym kiedykolwiek przejmował się rzeczywistym typem iteratora (no może kilka razy), tylko obsługiwane operacje ... Twierdziłbym, że redundancja składniowa rzadko jest dobrym pomysłem (DRY).
Grizzly,
16
btw google zmodyfikowało swoje głupie wytyczne, więc teraz w końcu zezwalają na auto
NoSenseEtAl

Odpowiedzi:

192

Istnieje kilka różnic między Boost.Thread a standardową biblioteką wątków C ++ 11:

  • Boost obsługuje anulowanie wątków, wątki C ++ 11 nie
  • C ++ 11 obsługuje std::async , ale Boost nie
  • Boost ma boost::shared_mutexblokadę dla wielu czytników / jednego zapisującego. Analogiczne std::shared_timed_mutexjest dostępne tylko od C ++ 14 ( N3891 ), natomiast std::shared_mutexjest dostępne tylko od C ++ 17 ( N4508 ).
  • Limity czasu C ++ 11 różnią się od limitów czasu Boost (chociaż powinno to wkrótce zmienić się teraz Boost.Chrono zostało zaakceptowane).
  • Niektóre nazwy są różne (np boost::unique_futurevs.std::future )
  • Semantyka przekazywania argumentów std::threadróżni się od funkcji boost::thread--- Boost boost::bind, które wymagają kopiowalnych argumentów. std::threadzezwala na typy tylko std::unique_ptrdo przenoszenia, takie jak przekazywanie jako argumentów. Ze względu na użycie boost::bindsemantyki symboli zastępczych, takich jak_1 w wyrażeniach zagnieżdżonych powiązań, również może być inna.
  • Jeśli nie jawnie wywołać join()lub detach()wówczas boost::threadoperator destructor i przypisanie wezwie detach()na obiekcie wątku niszczone / przypisany. W przypadku obiektu C ++ 11 std::threadspowoduje to wywołanie std::terminate()i przerwanie aplikacji.

Aby wyjaśnić kwestię parametrów tylko do przenoszenia, poniżej przedstawiono prawidłowy C ++ 11 i przenosi własność intz tymczasowego std::unique_ptrna parametr, w f1którym uruchamiany jest nowy wątek. Jeśli jednak użyjesz boost::thread, nie zadziała, ponieważ używa boost::bindwewnętrznie i std::unique_ptrnie można go skopiować. Istnieje również błąd w bibliotece wątków C ++ 11 dostarczonej z GCC, który uniemożliwia to działanie, ponieważ używa jej również std::bindw implementacji.

void f1(std::unique_ptr<int>);
std::thread t1(f1,std::unique_ptr<int>(new int(42)));

Jeśli używasz Boost, prawdopodobnie możesz stosunkowo bezboleśnie przełączyć się na wątki C ++ 11, jeśli twój kompilator to obsługuje (np. Najnowsze wersje GCC na Linuksie mają prawie kompletną implementację biblioteki wątków C ++ 11 dostępną w -std=c++0x trybie).

Jeśli Twój kompilator nie obsługuje wątków C ++ 11, możesz uzyskać implementację innej firmy, taką jak Just :: Thread , ale nadal jest to zależność.

Anthony Williams
źródło
1
Istnieją oddzielne metody blokowania / odblokowywania dla czytelników i pisarzy ( lock/ unlockdla pisarzy a „lock_shared / unlock_shared” dla czytelników). Wielu czytelników może wywoływać lock_shared bez blokowania, o ile żaden pisarz nie używa go.
Dave S
2
Dokumentacja shared_mutexjest dostępna pod adresem boost.org/doc/libs/1_47_0/doc/html/thread/… . Możesz zablokować mutex jako współdzielony lub wyłączny, a następnie użyć odpowiedniej funkcji odblokowania. Możesz również użyć do tego typów RAII ( shared_lockbierze wspólną blokadę odczytu lock_guardi unique_lockwyłączność). Próbowałem wyjaśnić kwestię typów tylko do ruchu.
Anthony Williams,
3
Jeszcze jedna drobna rzecz, która mnie zaskoczyła: w boosterze destruktor działającego wątku odłącza go ( boost.org/doc/libs/1_47_0/doc/html/thread/… ), podczas gdy w C ++, destruktor uruchomionego wątku kończy działanie () (FDIS 30.3.1.3)
Cubbi
3
W C ++ 11 try_scoped_lockfunkcje są objęte std::unique_lock. Istnieje konstruktor, który pobiera muteks i std::try_to_lock, a następnie wywoła try_lock()muteks zamiast lock(). Zobacz stdthread.co.uk/doc/headers/mutex/unique_lock/…
Anthony Williams
4
Tak, Boost. Odkąd to napisałem, wątek znacznie zbliżył się do standardu C ++ 11, głównie dzięki pracy Vicente Boteta.
Anthony Williams,
24

std::threadjest w dużej mierze wzorowany na boost::thread, z kilkoma różnicami :

  • semantyka, której nie można kopiować, mapowanie jednego uchwytu do jednego wątku systemu operacyjnego, jest zachowana. Ale ten wątek jest ruchomy, aby umożliwić powrót wątku z funkcji fabrycznych i umieszczenie go w pojemnikach.
  • Ta propozycja dodaje anulowanie do boost::thread , co jest znaczną komplikacją. Ta zmiana ma duży wpływ nie tylko na wątek, ale także na pozostałą część biblioteki wątków C ++. Uważa się, że ta duża zmiana jest uzasadniona korzyściami.
    • Destruktor wątków musi teraz wywoływać anulowanie przed odłączeniem, aby uniknąć przypadkowego wycieku wątków podrzędnych, gdy wątki nadrzędne są anulowane.
    • Wymagany jest teraz jawny element odłączający, aby umożliwić odłączanie bez anulowania.
  • Pojęcia dotyczące uchwytu wątku i tożsamości wątku zostały podzielone na dwie klasy (są w tej samej klasie boost::thread). Ma to na celu ułatwienie manipulacji i przechowywania tożsamości wątku.
  • Dodano możliwość stworzenia identyfikatora wątku, który gwarantuje porównywanie się równy żadnemu innemu wątkowi możliwemu do połączenia ( boost::threadnie ma tego). Jest to przydatne w przypadku kodu, który chce wiedzieć, czy jest wykonywany przez ten sam wątek, co poprzednie wywołanie (konkretnym przykładem są rekurencyjne muteksy).
  • Istnieją „tylne drzwi” umożliwiające uzyskanie rodzimego uchwytu wątku, tak aby klienci mogli w razie potrzeby manipulować wątkami przy użyciu podstawowego systemu operacyjnego.

To pochodzi z 2007 roku, więc niektóre punkty są już nieaktualne: boost::threadma native_handleteraz funkcję i, jak podkreślają komentatorzy, std::threadnie ma już anulowania.

Nie mogłem znaleźć żadnych znaczących różnic między boost::mutexi std::mutex.

Alex B.
źródło
2
std::threadnie ma anulowania; to jest boost::threadto!
Anthony Williams,
@Anthony czy na pewno nie masz na myśli interrupt()boost :: thread? Wygląda również na to, że jest to oryginalna propozycja, która zmieniła się od 2007 roku.
Alex B
4
Tak, anulowanie wzmocnienia nazywa się „przerwaniem”. Tak, to stara propozycja. Najnowszy publiczny projekt standardu C ++ 11 (który zawiera bibliotekę wątków) to open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
Anthony Williams
6

Jest jeden powód, dla którego nie warto migrować do std::thread.

Jeśli używasz linkowania statycznego, std::threadstaje się bezużyteczny z powodu tych błędów / funkcji gcc:

Mianowicie, jeśli zadzwonisz std::thread::detachlub std::thread::joindoprowadzi to do wyjątku lub awarii, podczas gdy boost::threaddziała dobrze w tych przypadkach.

ks1322
źródło
Widzę, że jeden błąd jest NIEPOTWIERDZONY, a drugi NIEPRAWIDŁOWY, z komentarzem mówiącym, że zgłaszający powinien był linkować libpthread.a . Czy jesteś absolutnie pewien tego, co mówisz?
einpoklum
1
@einpoklum, powinieneś być w stanie sprawić, by działało, używając Wl,--whole-archive -lpthread -Wl,--no-whole-archive, zobacz tę odpowiedź, na przykład stackoverflow.com/a/23504509/72178 . Ale nie jest to bardzo prosty sposób łączenia się z libpthread.apomysłem i również uważany za zły.
ks1322
4
Czy możemy założyć, że te błędy zostały naprawione, ponieważ jest teraz 2016? Błędy zostały opublikowane w 2012 roku, a od gcc 4.9.2 oficjalnie obsługuje C ++ 11, więc nie możemy narzekać na C ++ 11 przed oficjalnym wsparciem.
Splash
6

Sprawa Enterprise

Jeśli piszesz oprogramowanie dla przedsiębiorstwa, które musi działać na średniej lub dużej różnorodności systemów operacyjnych, a co za tym idzie, kompilować je z różnymi kompilatorami i wersjami kompilatorów (zwłaszcza stosunkowo starymi) w tych systemach operacyjnych, radzę trzymać się z daleka od C ++ 11 na razie w ogóle. Oznacza to, że nie możesz używać std::threadi polecam używanie boost::thread.

Podstawowy / Tech Startup Case

Jeśli piszesz dla jednego lub dwóch systemów operacyjnych, wiesz na pewno, że będziesz musiał budować tylko za pomocą nowoczesnego kompilatora, który w większości obsługuje C ++ 11 (np. VS2015, GCC 5.3, Xcode 7), a jeszcze nie jesteś zależne od biblioteki boost, std::threadmoże to być dobra opcja.

Moje doświadczenie

Osobiście jestem zwolennikiem zahartowanych, intensywnie używanych, wysoce kompatybilnych, wysoce spójnych bibliotek, takich jak boost kontra bardzo nowoczesna alternatywa. Jest to szczególnie ważne w przypadku skomplikowanych tematów programowania, takich jak wątki. Ponadto od dawna odniosłem wielki sukces z boost::thread(i ogólnie ulepszeniem) w szerokiej gamie środowisk, kompilatorów, modeli wątków itp. Kiedy wybieram, wybieram przyspieszenie.

Nicholas Smith
źródło
1
@UmNyobe On ma rację. Wiele implementacji wątków w C ++ 11 jest tak zepsutych, że jestem zaskoczony, że ludzie nawet rozważają jego użycie.
StaceyGirl
3

std::mutexWydaje się, że program Visual Studio 2013 zachowuje się inaczej niż program boost::mutex, co spowodowało pewne problemy (zobacz to pytanie ).

Robert Hegner
źródło
1

W odniesieniu do std :: shared_mutex dodanego w C ++ 17

Pozostałe odpowiedzi zapewniają bardzo dobry przegląd ogólnych różnic. Jest jednak kilka problemów związanych z std::shared_mutextym wzmocnieniem.

  1. Muteksy z możliwością aktualizacji. Tych nie ma std::thread. Umożliwiają one zmianę czytelnika na pisarza bez pozwolenia innym pisarzom na wejście przed tobą . Umożliwiają one wykonywanie takich czynności, jak wstępne przetwarzanie dużych obliczeń (na przykład ponowne indeksowanie struktury danych) w trybie odczytu, a następnie aktualizację do zapisu w celu zastosowania ponownego indeksowania, przytrzymując blokadę zapisu tylko przez krótki czas.

  2. Uczciwość. Jeśli masz ciągłą aktywność w czytaniu z a std::shared_mutex, twoi pisarze będą na czas nieokreślony zablokowani. Dzieje się tak, ponieważ jeśli pojawi się inny czytelnik, zawsze będzie miał pierwszeństwo. W przypadku boost:shared_mutexwszystkich wątków ostatecznie nadany zostanie priorytet. (1) Ani czytelnicy, ani pisarze nie będą głodni.

Tl; dr polega na tym, że jeśli masz system o bardzo dużej przepustowości, bez przestojów i bardzo dużej rywalizacji, std::shared_mutexnigdy nie zadziała bez ręcznego zbudowania systemu priorytetowego. boost::shared_mutexzadziała po wyjęciu z pudełka, chociaż w niektórych przypadkach może być konieczne majstrowanie przy nim. Twierdzę, że std::shared_mutexto zachowanie jest ukrytym błędem, który czeka na wystąpienie w większości kodu, który go używa.

(1) rzeczywiste algorytm wykorzystuje opiera się na scheduler wątku OS. Z mojego doświadczenia wynika, że ​​gdy odczyty są nasycone, w systemie Windows występują dłuższe przerwy (przy uzyskiwaniu blokady zapisu) niż w systemie OSX / Linux.

Robert Fraser
źródło
0

Próbowałem użyć shared_ptr z std zamiast boost i faktycznie znalazłem błąd w implementacji gcc tej klasy. Moja aplikacja ulegała awarii z powodu dwukrotnego wywołania destruktora (ta klasa powinna być bezpieczna dla wątków i nie powinna generować takich problemów). Po przejściu do boost :: shared_ptr wszystkie problemy zniknęły. Obecne implementacje C ++ 11 wciąż nie są dojrzałe.

Boost ma też więcej funkcji. Na przykład nagłówek w wersji standardowej nie zapewnia serializatora do strumienia (tj. Cout << duration). Boost ma wiele bibliotek, które używają własnych, itp. Odpowiedników, ale nie współpracują z wersjami standardowymi.

Podsumowując - jeśli masz już aplikację napisaną przy użyciu boosta, bezpieczniej jest zachować kod w takiej postaci, w jakiej jest, zamiast wkładać trochę wysiłku w przejście na standard C ++ 11.

user3323559
źródło
4
shared_ptrDestruktor nie musi być bezpieczny wątku, to niezdefiniowane zachowanie mieć jeden wątek z uzyskaniem dostępu do obiektu, podczas gdy inny wątek jest zniszczenie. Jeśli uważasz, że znalazłeś błąd w shared_ptr GCC, zgłoś go , w przeciwnym razie, biorąc pod uwagę prawdopodobieństwo, używasz go nieprawidłowo.
Jonathan Wakely