2012-03-16 18 views
31

Circular_buffer z biblioteki boost nie jest wątkowo bezpieczna. Więc zawinąłem boost :: circular_buffer obiekt w klasie, jak pokazano poniżej. Wzajemne wykluczenie między wątkami jest osiągane (jak sądzę) przez użycie zmiennych warunkowych, muteksa i blokady. Czy ten wątek implementacji jest bezpieczny?Bezpieczna implementacja wątku bufora kołowego

#include <boost/thread/condition.hpp> 
#include <boost/thread/mutex.hpp> 
#include <boost/thread/thread.hpp> 
#include <boost/circular_buffer.hpp> 

// Thread safe circular buffer 
template <typename T> 
class circ_buffer : private boost::noncopyable 
{ 
public: 
    typedef boost::mutex::scoped_lock lock; 
    circ_buffer() {} 
    circ_buffer(int n) {cb.set_capacity(n);} 
    void send (T imdata) { 
     lock lk(monitor); 
     cb.push_back(imdata); 
     buffer_not_empty.notify_one(); 
    } 
    T receive() { 
     lock lk(monitor); 
     while (cb.empty()) 
      buffer_not_empty.wait(lk); 
     T imdata = cb.front(); 
     cb.pop_front(); 
     return imdata; 
    } 
    void clear() { 
     lock lk(monitor); 
     cb.clear(); 
    } 
    int size() { 
     lock lk(monitor); 
     return cb.size(); 
    } 
    void set_capacity(int capacity) { 
     lock lk(monitor); 
     cb.set_capacity(capacity); 
    } 
private: 
    boost::condition buffer_not_empty; 
    boost::mutex monitor; 
    boost::circular_buffer<T> cb; 
}; 

Edit Jest to obecnie klasa szablon, który akceptuje obiekt dowolnego typu (nie tylko cv::Mat obiektu).

+4

Wygląda na to, że lepiej pasowałoby do [CodeReview] (http://codereview.stackexchange.com/). –

+1

Wybacz moje głupie pytanie, ale gdzie jest potrzebny bezpieczny bufor okrągły? We wszystkich punktach, w których kiedykolwiek używałem okrągłego bufora, byłoby złym błędem dostęp do niego z wielu takich wątków. Więc z czystej ciekawości, jaki jest twój przypadek użycia? – LiKao

+1

@LiKao Używam go do pobierania ramek z kamer sieciowych do MATLAB, zobacz mój poprzedni post http://stackoverflow.com/questions/9472880/how-to-implement-a-ccular-buffer-of-cvmat-objects-opencv . Jak podejdziesz do tego? – Alexey

Odpowiedz

16

Tak.
Jeśli zablokujesz wszystkie publiczne metody z tą samą blokadą, będą one bezpieczne dla wątków.

Możesz rozważyć użycie read-write locks, która może mieć lepszą wydajność, jeśli masz wielu równoczesnych czytników.

Jeśli nie masz dużej liczby czytników, po prostu dodasz obciążenie, ale warto sprawdzić opcje i testy.

+10

Nie sądzę, że zamki do odczytu i zapisu mają sens w kolistym buforze. Zarówno producenci, jak i konsumenci modyfikują bufor, więc wszyscy oni są w rzeczywistości * pisarzami *. –

+0

@ DavidRodríguez-dribeas - Masz rację w tej sprawie.Tak naprawdę nie weszłam do projektu, tylko część bezpieczeństwa nici. –

0

Wygląda dobrze na pierwszy rzut oka, z tym że w ogóle nie używasz warunku buffer_not_full. Prawdopodobnie chcesz dodać kod podobny do kodu buffer_not_empty.

+1

Jeśli źródło danych generuje więcej danych, niż można zmieścić w buforze, parametr boost :: circular_buffer zapisuje najstarsze dane. To jest wporządku. Tak więc warunek 'buffer_not_full' nie musi być sprawdzany. – Alexey

5

Myślę, że wygląda dobrze, z wyjątkiem tego, że istnieje kilka bezsensownych kopii Maty wykonanych w send. Nie potrzebujesz nowego, możesz bezpośrednio przekazać argument o numerze send do swojej cb.

+0

+1 za unikanie bezcelowego, kosztownego i zwiększającego rywalizację kopiowania. –

+0

@MartinJames Nie mogę bezpośrednio przekazać argumentu send. "Klasa cv :: Mat implementuje liczenie odwołań i płytką kopię tak, że gdy zdjęcie zostanie przypisane do innego, dane obrazu nie zostaną skopiowane, a oba obrazy wskażą ten sam blok pamięci." "Licznik odwołań jest utrzymywany tak, że pamięć zostanie zwolniona tylko wtedy, gdy wszystkie odniesienia do obrazu zostaną zniszczone.Jeśli chcesz utworzyć obraz, który będzie zawierał nową kopię oryginalnego obrazu, użyjesz metody copyTo(). " - z "Książki kucharskiej aplikacji OpenCV 2 Computer Vision" (strona 28). – Alexey

+1

Nadal nie potrzebujesz nowego w tym przypadku, przydzielanie nowego obrazu na stosie działa równie dobrze. Ale czy nie jest to ta funkcja, której potrzebujesz, udostępniona kopia w twoim okrągłym buforze? –

2

Twoja implementacja jest podobna do tej przedstawionej w tym dokumencie: blogger. Powinieneś przeczytać tego bloga, aby sprawdzić, czy przegapiłeś coś w swojej implementacji.

Jeśli twoje obiekty Mat są kosztowne w tworzeniu/kopiowaniu, powinieneś unikać ich ciągłego tworzenia/kopiowania/usuwania. Zamiast tego powinieneś mieć pulę (aka wolną listę) obiektów Mat, które nieustannie pobierają z recyklingu w jakiejś architekturze potoku. Opisuję tego typu architekturę w tym answer do pokrewnego pytania.

W tej odpowiedzi zasugerowałem użycie stosu blokującego do implementacji puli, ale można również użyć blokowania circular_buffer. Powodem, dla którego zasugerowałem stos, było to, że uważałem, że może to być bardziej przyjazne dla pamięci podręcznej, ale nigdy nie zmierzyłem, aby sprawdzić, czy to coś zmieni.

+0

Obiekty matowe (obrazy) nie są zbyt duże i mają prawie taki sam rozmiar podczas każdego połączenia. Właśnie zdałem sobie sprawę, że mogę przydzielić obiekty Mat na stosie. – Alexey

+0

Używam pule obiektów (na podstawie blokowania kolejek), przez długi czas. W rzeczywistości używam wzorca mijania poolObject + prawie wyłącznie dla moich projektów aplikacji wielowątkowych. Bezkopiowanie, brak-malloc, bez-GC i wbudowana kontrola przepływu to nie jedyne zalety. W aplikacjach GUI tworzenie pul obiektów przed wszelkimi formularzami lub wątkami roboczymi oznacza, że ​​zazwyczaj zapominam o destrukcji puli jawnej lub problemach z kończeniem wątków. Zrzucanie poziomów puli na liczniku pokazuje wycieki bez nieszczęśliwych zewnętrznych narzędzi do wycieków, takich jak V *******, które spowalniają aplikację wole do indeksowania. –

+0

Domyślnie konstruktor kopii cv :: Mat tworzy płytką kopię. – Reunanen

Powiązane problemy