2010-03-26 13 views
6

W boost::shared_ptr destructor, odbywa się to:Dlaczego licznik odwołań w boost :: shared_ptr volatile?

if(--*pn == 0) 
{ 
    boost::checked_delete(px); 
    delete pn; 
} 

gdzie pn jest wskaźnik do licznika odniesienia, który jest typedefed jak

shared_ptr::count_type -> detail::atomic_count -> long 

Liczyłam na long być volatile long, biorąc pod uwagę wątkowe użycie i nie-atomowe 0-sprawdzanie-i-usuwanie w powyższym destruktorze shared_ptr. Dlaczego to nie jest niestabilne?

Edycja:

Okazuje się, że spojrzał na nagłówka, używanego podczas wielowątkowy użycie nie jest określona (atomic_count.hpp). W atomic_count_win32.hpp dekrement jest poprawnie implementowany do użycia wielowątkowego.

+0

Skąd się wziął ten kod? – jalf

+0

@jalf: shared_ptr_nmt.hpp –

Odpowiedz

16

Ponieważ volatile nie jest konieczne do wielowątkowości i nie robi nic korzystnego, ale potencjalnie niszczy wiele optymalizacji.

Aby zapewnić bezpieczny dostęp wielowątkowy do zmiennej, prymitywą potrzebną jest bariera pamięci, która zapewnia zarówno gwarancję volatile, jak i kilka innych (zapobiega ponownemu porządkowaniu pamięci przez barierę, która jest niestabilna do)

Wierzę, że shared_ptr używa operacji atomowych, gdy jest to możliwe, co niejawnie zapewnia barierę pamięci. W przeciwnym razie powraca do muteksu, który również zapewnia barierę pamięci.

Zobacz Why is volatile not considered useful in multithreaded C or C++ programming? lub http://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming/ więcej szczegółów

Edit
count_type jest nielong w ogólnym przypadku. Jest to zamienny na long. Jeśli spojrzysz w atomic_count.hpp, typedef na long jest stosowany tylko wtedy, gdy nie ma dostępnego wątku (w takim przypadku oczywiście nie jest wymagana synchronizacja). W przeciwnym razie używa implementacji zdefiniowanej w boost/smart_ptr/detail/atomic_count_pthreads.hpp lub boost/smart_ptr/detail/atomic_count_win32.hpp lub jednego z pozostałych wymienionych plików. Są to zsynchronizowane klasy opakowujące, które zapewniają, że wszystkie operacje są wykonywane atomowo.

+0

"Wierzę, że shared_ptr używa operacji atomowych, gdy jest to możliwe" - nie w cytowanym przykładzie z destruktorem, w którym obecnie leży mój problem ... –

+0

Cóż, ustawienie zmiennej 'volatile' nie zmieniłoby niczego. Masz rację, zmniejszanie i porównywanie długiego zera wydaje się niebezpieczne, ale zależy to od tego, co robi * reszta * klasy wskaźnika. – jalf

+0

@jalf: Cóż, to ty mnie zgubiłeś.;-) Właśnie teraz, w moim przykładzie, dwa wątki wpisują dtor shared_ptr, a "reszta klasy wskaźnika" nie robi nic innego. –

8

volatile ma praktycznie nic wspólnego z gwintowaniem. Zobacz here.

+0

Czy można powiedzieć, że nie ma problemu z powyższym traktowaniem licznika w destruktorze, jeśli jest on wykonywany na różnych wątkach? –

2

Źle odczytujesz kod. atomic_count jest zdefiniowany po prostu jako długi, jeśli kod nie używa wielowątkowości:

#ifndef BOOST_HAS_THREADS 

namespace boost 
{ 

namespace detail 
{ 

typedef long atomic_count; 

} 

} 

#elif //... include various platform-specific headers that define atomic_count class 

#endif 
+0

Dzięki! Edytował pytanie, aby wyjaśnić pewne kwestie. (Przyjął już odpowiedź Jalfa) –

Powiązane problemy