2012-12-19 21 views
16

Przeczytałem on Stackoverflow, że żaden z pojemników STL nie jest bezpieczny w użyciu dla pisania. Ale co to oznacza w praktyce? Czy to oznacza, że ​​powinienem przechowywać zapisywalne dane w zwykłych tablicach?Bezpieczeństwo wątków pisania std :: vector vs plain array

Spodziewam się, że jednoczesne wywołania do std::vector::push_back(element) mogą prowadzić do niespójnych struktur danych, ponieważ może to pociągać za sobą zmianę rozmiaru wektora. Ale co o takim wypadku, w którym zmiana rozmiaru nie jest zaangażowany:

1) za pomocą tablicy:

int data[n]; 
// initialize values here... 

#pragma omp parallel for 
for (int i = 0; i < n; ++i) { 
    data[i] += func(i); 
} 

2) przy użyciu `std :: vector``:

std::vector<int> data; 
data.resize(n); 
// initialize values here... 

#pragma omp parallel for 
for (int i = 0; i < n; ++i) { 
    data[i] += func(i); 
} 

Czy pierwsza implementacja jest naprawdę lepsza od drugiej: a) pod względem bezpieczeństwa gwintów i b) pod względem wydajności? Wolałbym używać std :: vector, ponieważ mniej mi się podobają tablice w stylu C.

EDYCJA: Usunąłem #pragma omp atomic update chroniącego zapis.

+1

Nie jestem wystarczająco pewny, aby zrobić odpowiedź, ale jestem pewien, że pisanie do różnych elementów 'std :: vector' jest wątkowo bezpieczne. – Angew

+1

Te dwa fragmenty są równie bezpieczne dla wątków. –

+0

"Ale co to oznacza w praktyce?" : Oznacza to, że pojemnik musi być zatrzaśnięty wyłącznie dla zapisu * i * odczytu, jeśli dowolna/każda operacja jest zbieżna z równoczesnym ** zapisaniem **. Możesz kazać wszystkim czytelnikom walić w pojemnik, który chcesz, ale jak tylko zostanie wprowadzony potencjał * zapisu, wszystkie zakłady są wyłączone i musisz zablokować * dostęp do wszystkich * (nie tylko innych autorów). Blokada z pojedynczym zapisem i wieloma odczytami działa dobrze w tym przypadku. – WhozCraig

Odpowiedz

22

Oba są równie bezpieczne. Pod warunkiem, że żaden element nie jest dostępny z wielu wątków, jesteś w porządku. Twoja równoległa pętla będzie miała dostęp do każdego elementu tylko raz, a więc tylko z jednego wątku.

W standardzie jest miejsce na funkcje elementów kontenerów, aby nie były bezpieczne dla wątków. W tym przypadku korzystasz z vector<int>::operator[], więc potrzebujesz wyraźnej gwarancji bezpieczeństwa wątków dla tego elementu, co wydaje się uzasadnione, ponieważ wywoływanie go nawet na niestałym wektorze nie modyfikuje samego wektora. Tak więc wątpię, że jest w tym przypadku problem, ale nie szukałem gwarancji [edytuj: rici ją znalazła]. Nawet jeśli jest to potencjalnie niebezpieczne, można wykonać przed pętlą int *dataptr = &data.front(), a następnie indeksować wartość dataptr zamiast data.

Na marginesie ten kod to , a nie gwarantowany jako bezpieczny dla vector<bool>, ponieważ jest to specjalny przypadek, w którym wiele elementów współistnieje w jednym obiekcie. Byłby bezpieczny dla tablicy bool, ponieważ różne elementy tego są różne "lokalizacje pamięci" (1.7 w C++ 11).

+0

Napisałem małą funkcję testową, aby zobaczyć, czy openmp działa poprawnie. Użyłem 'vector ', zmieniłem rozmiar do liczby używanych wątków (zainicjalizowanych na false) i ustawiłem wszystkie wartości na true jednocześnie. Jeśli każda wartość była prawidłowa, funkcja zwróciła wartość true, aby pokazać, że wszystkie wątki zostały poprawnie utworzone. 'wektor ' uderzył ponownie. Na szczęście ostatnio przeczytałem odpowiednią część standardu, więc mogłem to zrozumieć, ale jeśli ktoś się o to potknie, muszą upewnić się, że unikają równoczesnych zapisów do 'wektora '. – Justin

+0

przechodząc przez to, powinno być bezpieczne push_back do poszczególnych wektorów pod 'std :: vector >' z każdego odpowiedniego osobnego wątku? –

1

Najważniejsze jest to, że jeśli masz wiele wątków uzyskujących dostęp do wektora, nie możesz polegać na C++, aby nie uszkodzić struktury danych za pomocą wielu jednoczesnych zapisów. Musisz więc użyć jakiejś straży. Z drugiej strony, jeśli twój program nie używa wielu wątków, ponieważ twoje przykłady nie wydają się, jesteś w porządku.

+5

Jego przykład używa wielu wątków za pomocą OpenMP – nogard

+2

Upvoted to 0. Prawdopodobnie został odrzucony jako niepoprawny, ale został opublikowany przed usunięciem edycji: #pragma omp aktualizacji atomowej, która zapobiega równoczesnemu zapisywaniu i sprawia, że ​​ta odpowiedź jest poprawna w kontekście Oryginalny post –

1

W takim przypadku powinieneś po prostu zbudować wektor z niezbędną liczbą wartości? i wszystko będzie działać dobrze.

std::vector<int> data(n, 0); 

resize() działa dobrze. Wydajność będzie równa. Powód, dla którego dostęp wielowątkowy nie powoduje uszkodzenia wektora, jest następujący: dane są zlokalizowane i nie będą się z nich przesuwać. Wątki OMP nie będą miały dostępu do tego samego elementu naraz.

17

Dla C++ 11, który określa zasady dla wyścigów danych, opisane jest bezpieczeństwo wątków kontenerów. Odpowiednią sekcją normy jest § 23.2.2, akapit 2:

Niezależnie od (17.6.5.9), implementacje są wymagane w celu uniknięcia wyścigów danych, gdy zawartość zawartego obiektu w różnych elementach w tej samej sekwencji, z wyjątkiem wektora <bool>, jest modyfikowana jednocześnie.

[Uwaga: w przypadku wektora <int> x przy rozmiarze większym niż jeden, x [1] = 5 i * x.begin() = 10 mogą być wykonywane jednocześnie bez wyścigu danych, ale x [0] = 5 i * x.begin() = 10 wykonywane jednocześnie mogą spowodować wyścig danych. Jako wyjątek od zasady ogólnej dla wektora <bool> y, y [0] = true może ścigać się z y [1] = true. -end uwaga]

Wspomniany § 17.6.5.9 zasadniczo zakazuje jednoczesnego modyfikacji przez każdego standardowego interfejsu biblioteki, chyba że wyraźnie dozwolone, więc odcinek cytuję powie dokładnie to, co jest dozwolone (i która obejmuje swoją zużycia).

Ponieważ kwestia została podniesiona przez Steve Jessop ustępie 1 § 23.2.2 wyraźnie umożliwia równoczesne stosowanie [] w pojemnikach kolejności:

Dla celów unikania wyścigi dane (17.6.5.9), wdrożeń uwzględnia następujące stałe funkcje: początek, koniec, rbegin, rend, front, back, data, find, lower_bound, upper_bound, equal_range, at i, z wyjątkiem asocjatywnych lub nieuporządkowanych kontenerów asocjacyjnych operator [].