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ć.
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ć. –
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! –
@ChrisBeck dzięki za twoją próbę wyjaśnienia. Proszę, abyś rozwiązał problem i jego rozwiązanie w "odpowiedzi". –