2011-10-19 14 views
44

W szczególności szukam kolejki blokującej. Czy jest coś takiego w C++ 11? Jeśli nie, jakie są moje inne opcje? Naprawdę nie chcę już więcej wchodzić w poziom wątku. Zbyt podatny na błędy.Czy są jakieś współbieżne pojemniki w C++ 11?

+2

+1, Interesujący Q.Scott Meyers zapytał o to w C++ 0x dni [tutaj] (http://www.velocityreviews.com/forums/t732306-concurrent-containers.html) .To byłoby interesujące wiedzieć jak to zmieniło się po C++ 11. –

+0

Bardzo łatwo zmienić kolejkę standardową w kolejkę blokującą za pomocą prymitywów –

Odpowiedz

32

According to Diego Dagum from Microsoft's Visual C++ Team:

pytanie o charakterze powtarzającym się (no, jednym z wielu) jest o pojemnikach STL i czy są bezpieczne dla wątków.

Biorąc słowa Stephana tutaj, w rzeczywistości jest to, że nie są one, a nie jako bug ale jako cechę: każdy mający funkcję składową każdego kontenera STL przejmującej wewnętrzną blokadę by zniszczyć wydajność. Jako uniwersalna biblioteka wielorazowego użytku, w rzeczywistości nie zapewni on poprawności: poprawny poziom umieszczania blokad to określony przez program. W tym sensie poszczególne funkcje składowe nie mają zazwyczaj takiego właściwego poziomu.

The Parallel Patterns Library (PZP) zawiera wiele pojemników, które zapewniają bezpieczny dostęp do gwintu na ich elementy:

  • concurrent_vector Class jest sekwencją klasy pojemnik, który umożliwia swobodny dostęp do każdego elementu. Pozwala na bezpieczne współbieżne dołączanie, dostęp do elementu, dostęp do iteratora i operacje przejścia iteratora.
  • concurrent_queue Class jest klasą kontenerów sekwencji, która umożliwia dostęp do jej elementów po raz pierwszy, pierwszy z zewnątrz. Umożliwia on ograniczony zestaw bezpiecznych operacji, takich jak push i try_pop, aby wymienić tylko kilka.

Niektóre próbki here.

Również interesująca: http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html.

+0

yeach. ale problemem jest - to tylko dla windows = ( –

1

Interfejsy kontenerów po prostu nie zostały zaprojektowane w tym celu. Dla interfejsów, z których korzystają, blokada widoczna dla klienta jest naprawdę jedynym sposobem, w jaki można to osiągnąć, gwarantując jednocześnie poprawność i przewidywalne zachowanie. Byłoby to również bardzo nieefektywne, ponieważ liczba przejęć byłaby bardzo wysoka (w stosunku do dobrej realizacji).

Roztwór 1

Pass od wartości (w razie potrzeby).

Rozwiązanie 2

Utwórz kolekcję proste przykręcane implementacji, których można użyć, aby przejść pojemników trzymając blokadę Zakres (rozważyć to pseudo C++):

template <typename TCollection> 
class t_locked_collection { 
public: 
    t_locked_collection(TCollection& inCollection, t_lock& lock) : collection(inCollection), d_lock(lock), d_nocopy() { 
    } 

    TCollection& collection; 
    // your convenience stuff 
private: 
    t_scope_lock d_lock; 
    t_nocopy d_nocopy; 
}; 

następnie rozmówcy łączy zamek z kolekcją, a następnie aktualizujesz swoje interfejsy, aby użyć (przekazać) typu kontenera tam, gdzie to właściwe. To tylko rozszerzenie klasy biednego człowieka.

Ten zamknięty pojemnik jest prostym przykładem i istnieje kilka innych wariantów.To jest wybrana przeze mnie droga, ponieważ naprawdę pozwala na użycie poziomu ziarnistości, który jest idealny dla twojego programu, nawet jeśli nie jest tak przejrzysty (syntaktycznie) jak zablokowane metody. Stosowanie istniejących programów jest stosunkowo łatwe. Przynajmniej zachowuje się w przewidywalny sposób, w przeciwieństwie do kolekcji z wewnętrznymi zamkami.

Innym wariantem byłoby:

template <typename TCollection> 
class t_lockable_collection { 
public: 
// ... 
private: 
    TCollection d_collection; 
    t_mutex d_mutex; 
}; 

// example: 
typedef t_lockable_collection<std::vector<int> > t_lockable_int_vector; 

... gdzie to typ podobny do t_locked_collection mogłyby zostać wykorzystane w celu odsłonięcia kolekcji podstawowej. Nie oznacza to, że podejście to jest niezawodne, po prostu głupie.

+0

z "przekazać według wartości", ponieważ masz zamiar przekazać kompletny pojemnik według wartości w celu utworzenia kopii i pracy na kopii? Lub przekazać elementy pojemnika przez wartość? Proszę opracować – steffen

+0

@steffen przekazuje elementy kontenera według wartości, biorąc pod uwagę interfejs wielu kontenerów, to (** Rozwiązanie 1 **) jest dalekie od optymalnego rozwiązania, podejście to jest również prawie bezużyteczną poprawnością wrt, chyba że jesteś skłonny napisać tony obsługi wyjątków i używaj mnóstwa współdzielonych wskaźników – justin

8

C++ 11 nie dostarcza samodzielnie pojemników jednocześnie. Istnieją jednak opcje biblioteczne. Oprócz wspomnianej już licencji PPL, nie zapomnij o bibliotece Intel TBB.

Ma jednoczesną implementację queue, hash_map, , set i vector. Ale to nie tylko bezpieczna dla wątków biblioteka kontenerów, ale także równoległa wersja standardowych algorytmów (pętla for, redukcja, sortowanie, ...).

Intel TBB website

+1

Czy możesz podać mi link współbieżnego zestawu? – user

2

Dziwię się, że nikt nie wspomniał moodycamel::ConcurrentQueue. Używamy go od dłuższego czasu i działa bardzo dobrze. Specyficzne jest to, że jego wdrożenie jest bezpieczne, co natychmiast przynosi ogromną prędkość. Inne powody jego używania (cytowanie z oficjalnej strony):

Nie ma zbyt wielu pełnowymiarowych kolejek wolnych od blokady dla C++. Wzmocnienie ma jeden, ale jest ograniczony do obiektów z trywialnymi operatorami przypisania i trywialnych destruktorów, na przykład. Kolejka TBB Intela nie jest wolna od blokady i wymaga również trywialnych konstruktorów. Istnieje wiele prac naukowych, które implementują blokady w C++, ale użyteczny kod źródłowy jest trudny do znalezienia, a testy jeszcze bardziej.

Niektóre odniesienia i porównania są dostępne here, here i here.

+0

Problem z implementacją moodycamel polega na tym, że nie jest to FIFO (tj. kolejność popped elementów nie jest gwarantowana tak jak kolejność pchanych elementów), więc nie jest to rozwiązanie uniwersalne. –

Powiązane problemy