2016-01-01 39 views
5

stąd: (!) Logic error in my defined Mutex class and the way I use it in producer consumer program - pthreadsDlaczego przekazywanie odniesienia do klasy Mutex nie jest dobrym projektem?

drodze mijamy wokół odnośników do twojej klasy mutex jest wyraźnie kłopoty, to wymyka się jakiejkolwiek hermetyzacji.

Dlaczego to jest problem? Czy powinienem podać wartość, a następnie napisać konstruktor kopii?

Czy w tym przypadku może wystąpić brak enkapsulacji? Jak powinienem enkapsulować co?

Również Why is passing references to the Mutex class not a good design?

Przechodząc odniesienie do zamka jest zły pomysł - nie „używać” blokada, tylko nabyć, a następnie podać je z powrotem. Przenoszenie go utrudnia śledzenie wykorzystania (krytycznego) zasobu. Przekazywanie odwołania do zmiennej mutex zamiast do blokady może nie być takie złe, ale wciąż utrudnia określenie, które części programu mogą występować w martwym punkcie, a więc coś, czego należy unikać.

Proszę wyjaśnić w prostym języku z przykładami - dlaczego przekazywanie referencji jest złym pomysłem?

+1

Cóż, zwykle używasz muteksów w wyjątkowo ostrożny sposób. Kiedy blokujesz muteks, chcesz, aby blokada miała bardzo krótki i starannie wybrany czas trwania, aby nie spowodować zakleszczenia. Przekazywanie referencji do zamka jest złym pomysłem - nie "korzystasz" z zamka, tylko nabywasz, a potem oddajesz go. Przenoszenie go utrudnia śledzenie wykorzystania (krytycznego) zasobu. Przekazywanie odwołania do zmiennej mutex zamiast do blokady może nie być takie złe, ale wciąż utrudnia określenie, które części programu mogą występować w martwym punkcie, a więc coś, czego należy unikać. Zwykle nigdy nie musisz tego robić. –

+0

Także, jeśli specjalnie przekazujesz "pthread_mutex &" do klasy pakowania mutex, mam na myśli, że jest to brzydkie ... dla jednego, możesz uzyskać zwisające odwołanie, jeśli nie jesteś ostrożny, a dla dwóch, jesteś wtedy związany do implementacji pthread. Prawdopodobnie celem twojej klasy jest zatuszowanie szczegółów implementacji, ale tutaj rażąco ich przeciekasz! –

+0

@ChrisBeck dzięki za twoją próbę wyjaśnienia. Proszę, abyś rozwiązał problem i jego rozwiązanie w "odpowiedzi". –

Odpowiedz

3

Oddałbym to do osobnych pytań: 1. Kiedy należy przekazywać przedmioty przez odniesienie? 2. Kiedy należy udostępniać muteks?

  1. Sposób przekazać obiekt jako argument odzwierciedla, jak można oczekiwać, aby dzielić się żywotność obiektu między dzwoniącego i odbierającym. Jeśli przejedziesz przez referencję, musisz założyć, że użytkownik będzie korzystał z obiektu tylko przez czas trwania połączenia lub, jeśli referencja jest przechowywana przez wywoływanego, czas życia uczestnika jest krótszy niż wartość odniesienia. Jeśli mamy do czynienia z dynamicznie przydzielanymi obiektami, prawdopodobnie powinniśmy używać inteligentnych wskaźników, które (między innymi) umożliwiają wyraźniejsze komunikowanie swoich intencji (patrz: Herb Sutter's treatment tego tematu).

  2. Należy unikać udostępniania muteksa. Jest to prawdą, niezależnie od tego, czy przekazywane przez odniesienie lub w jakikolwiek inny sposób. Udostępniając muteks, obiekt sam się wewnętrznie wpływa na zewnętrzną jednostkę. To narusza podstawową enkapsulację i jest wystarczającym powodem. (Zobacz dowolny tekst na temat programowania obiektowego dla zalet enkapsulacji). Prawdziwą konsekwencją dzielenia się muteksem jest prawdopodobieństwo impasu.

    Weź ten prosty przykład:

    • posiada MUTEX
    • akcji mutex z B
    • B uzyskuje blokadę przed wywołanie funkcji podniecają
    • Funkcja A za próbuje uzyskać blokadę
    • Zakleszczenie ..

Z perspektywy projektowania, dlaczego chciałbyś udostępnić muteks? Muteks chroni zasób, do którego dostęp może mieć wiele wątków. Że muteks powinien być ukryty (enkapsulowany) w klasie kontrolującej ten zasób. Mutex jest tylko jednym ze sposobów, w jaki ta klasa może chronić zasoby; jest to szczegół implementacji, o którym powinna wiedzieć tylko klasa. Zamiast tego udostępnij instancję klasy, która kontroluje zasób i pozwól mu zapewnić bezpieczeństwo wątku w sobie w dowolny sposób.

3

Myślę, że to zła abstrakcja, a także złe enkapsulacji. a mutex jest zwykle domyślnie skonstruowany z usuniętymi konstruktorami kopiowania, mając wiele obiektów mutex, które odnoszą się do tego samego obiektu logicznego, jest podatny na błędy, tj. może prowadzić do zakleszczeń i innych warunków wyścigowych, ponieważ programista lub czytnik może założyć, że są one różnymi jednostkami.

Ponadto, określając, z jakiego muteksu wewnętrznego korzystasz, ujawniasz szczegóły implementacji swoich wątków, tym samym łamiąc abstrakcję klasy Mutex. Jeśli używasz pthread_mutex_t, najprawdopodobniej będziesz używał wątków jądra (pthreads).

Enkapsulacja jest również zerwana, ponieważ twój Mutex nie jest pojedynczym enkapsulowanym obiektem, ale raczej rozproszonym na kilka (ewentualnie zwisających) odniesień.

Jeśli chcesz hermetyzacji pthread_mutex_t do klasy byś go jako tak

class Mutex { 
public: 
    void lock(); 
    void unlock(); 

    // Default and move constructors are good! 
    // You can store a mutex in STL containers with these 
    Mutex(); 
    Mutex(Mutex&&); 
    ~Mutex(); 

    // These can lead to deadlocks! 
    Mutex(const Mutex&) = delete; 
    Mutex& operator= (const Mutex&) = delete; 
    Mutex& operator= (Mutex&&) = delete; 

private: 
    pthread_mutex_t internal_mutex; 
}; 

obiekty MUTEX mają być udostępniane w udostępnionym zakresie deklarowanych w plikach wdrożeniowych zamiast ona zadeklarowana lokalnie i przekazywane w funkcjach jako odniesienie. Idealnie byłoby przekazać tylko argumenty do konstruktora wątków, którego potrzebujesz. Przekazywanie odniesień do obiektów zadeklarowanych w zakresie na tym samym "poziomie" co dana funkcja (wykonanie wątku w tym przypadku) zwykle prowadzi do błędów w kodzie. Co się stanie, jeśli zakres, w którym zostanie zadeklarowany muteks, już nie istnieje? Czy destruktor z mutex unieważni wewnętrzną implementację muteksu? Co się stanie, jeśli muteks przejdzie przez cały inny moduł, gdy zostanie przekazany i ten moduł rozpocznie swoje własne wątki i pomyśli, że muteks nigdy się nie zablokuje, co może prowadzić do nieprzyjemnych zakleszczeń.

Również jednym przypadkiem, w którym chciałbyś użyć konstruktora ruchu mutex, jest wzorzec fabuły mutex, jeśli chcesz utworzyć nowy muteks, wykonasz wywołanie funkcji, a funkcja zwróci muteks, który następnie dodaj do utraconych muteksów lub przejdź do wątku, który żąda tego poprzez jakieś współdzielone dane (wspomniana lista byłaby dobrym pomysłem dla tych współdzielonych danych).Uzyskanie takiego wzoru fabryki muteksów może jednak być dość trudne, ponieważ trzeba zablokować dostęp do wspólnej listy muteksów. Powinno to być fajne!

Jeśli intencją autora było uniknięcie zakresu globalnego, wówczas zadeklarowanie go w pliku implementacji jako obiektu statycznego powinno wystarczyć.

+0

wielkie dzięki. Poświęciłem czas, aby zrozumieć twoją odpowiedź. Zrozumiałem, że jeśli użyjemy konstruktora ruchu zamiast przekazywać odniesienie, nie będziemy musieli się martwić, że zewnętrzny obiekt lokalny zostanie usunięty. Czy to jest poprawne? Czy jest coś innego niż to, co chciałeś przekazać za pośrednictwem swojej odpowiedzi? –

+0

Tak, chciałem przekazać inne rzeczy poprzez moją odpowiedź. Przede wszystkim konstruktory ruchu są przydatne z muteksami nie dlatego, że sam chciałbyś je przekazać, ale w przypadku, gdy powiesz wektor muteksów i zmienisz rozmiar wektora, aby dodać nowe muteksy, muteks musi zostać przeniesiony z jednej tablicy do drugiego. Drugim głównym punktem, który chciałem przekazać, było. W większości przypadków obiekt mutex powinien być globalny. Trzecim głównym punktem, który chciałem przekazać, było to, że przekazanie muteksu przez referencję wymaga podania warunku wyścigu. – Curious

+0

Dziękuję! Mogę opublikować nagrodę hhahaha. Ale z powrotem do trzeciego punktu. Osobiście napisałem ten kod w moim pierwszym równoległym projekcie, więc mówię z doświadczenia. Miałem następujący kod ustawiony. W wątku, jeśli był dostęp do bloku dysku, który nie był powiązany z muteksem, użyłem wzorca fabrycznego, aby dać mi nowy obiekt mutex, a następnie przekazałem go do konstruktora wątków jako odniesienie. Następnie przystąpiłem do odrywania nici. To spowodowało więc wyścig w zniszczeniu muteksu i nie byłem szczęśliwy :(Mam nadzieję, że zwróciło to twoją wątpliwość w porządku :) – Curious