2009-08-02 11 views
6

Jeśli muteks jest zdefiniowany w funkcji, czy jego blokada ma zastosowanie do funkcji wywoływanych z tej funkcji? tj.Czy blokada muteksu dotyczy również wywoływanych funkcji?

void f() { 
    Mutex mutex; 
    g(); 
} 

Czy zamek nadal stosuje się do modyfikacji danych wg()?

Czy mam rację, mówiąc, że blokada zdefiniowana w metodzie klas dotyczy tylko określonych instancji tej klasy? Znaczenie:

Class Foo; 
Foo foo1, foo2; 
(In thread 1) foo1.bar(); 
(In thread 2) foo2.bar(); 

Czy każde połączenie mogło występować jednocześnie?

Byłby to niezła premia, gdyby ktoś mógł wyjaśnić/wskazać linki wyjaśniające mechanizm muteksów. Dzięki! Obecnie pracuję z biblioteką wątków Qt, jeśli ta informacja pomaga.

Odpowiedz

15

W twoim przykładzie nie blokujesz muteksu, więc nie zapobiegnie to, aby różne wątki miały dostęp do funkcji w tym samym czasie. Deklarujemy również mutex lokalnie wewnątrz funkcji, tak aby każde wywołanie funkcji używało innego lokalnego obiektu mutex. Nawet jeśli ten muteks byłby zablokowany, każde wywołanie funkcji blokowałoby inny obiekt mutex, nie uniemożliwiając równoczesnego dostępu.

Lepszą strategią byłoby setup tak:

class A { 
    QMutex mutex; 

    void f() { 
    QMutexLocker ml(mutex); // Acquire a lock on mutex 
    g(); 

    // The lock on the mutex will be released when ml is destroyed. 
    // This happens at the end of this function. 
    } 

    // ... 
}; 

W tym przypadku mutex jest zablokowana tak długo jak ml istnieje, więc również w czasie, gdy wątek spędza wewnątrz g(). Jeśli inny wątek wywołałby w tym czasie numer f(), zablokowałby on utworzenie obiektu ml, dopóki pierwszy wątek nie opuściłby tej funkcji, a nowy wątek może uzyskać blokadę mutex.

+1

+1 przy użyciu QT api –

7

Mutex to coś, co chwytasz, i zatrzyma wszelkie inne wątki próbujące go chwycić, dopóki nie zwolnisz go z wątku.

W swoim pytaniu masz funkcję f alokowania instancji Mutex. To nie wystarczy, aby go zablokować. Musisz wywołać mutex.lock() (w Qt, ale także ogólnie, chyba że używasz pthread, w tym przypadku użyj pthread_mutex_lock i baw się z niskopoziomowymi, zależnymi od platformy materiałami, Qt bardzo dobrze je streszcza).

tutaj jest przykład z Qt

void MyClass::doStuff(int c) 
    { 
     mutex.lock(); 
     a = c; 
     b = c * 2; 
     mutex.unlock(); 
    } 

Gdy pojawi się zamek, wezwanie do g() zostanie wykonana z wątku, który dostał blokadę, więc to będzie sama w tej rozmowy zakładającej, że nie wywołujesz g() z innych wątków z innej części kodu. Blokowanie nie oznacza, że ​​zatrzyma wszystkie pozostałe wątki. Spowoduje to przerwanie wątków próbujących uzyskać tę samą blokadę, aż do zwolnienia blokady.

Jeśli jest to jedyny sposób, w jaki twoje wątki docierają do g(), jesteś zsynchronizowany podczas tego dostępu.

W drugiej części pytania, jeśli muteks jest atrybutem instancji, będą to dwa różne muteksy. Będziesz musiał zadeklarować i utworzyć instancję klasy mutex i odwołać się do niej w celu zablokowania. W takim przypadku każda próba wywołania metody w klasie, która blokuje mutex klasy, będzie skutecznie zsynchronizowana, co oznacza, że ​​żadne dwa wątki nie wykonają tej metody razem.

Na przykład (nie mam Qt, więc nie mogę skompilować ten kod, a ja przestałem kodowania z nim 2 lata temu, więc nie może pracować)

class Foo { 
public: 
    void method(void) { 
     mutex.lock(); 
     cout << "method called"; 
     // long computation 
     mutex.unlock(); 
    } 

private: 
    QMutex mutex; 
}; 

Ok, w takim przypadku, załóżmy, że masz dwa wątki, 1 i 2 oraz dwa wystąpienia klasy Foo, aib. Załóżmy, że wątek 1 wywołuje a.method(), a wątek 2 wywołuje b.method(). W tym przypadku dwa muteksy są różnymi instancjami, więc każdy wątek wykonuje wywołanie niezależnie i działa równolegle.

Załóżmy, że masz dwa wątki, 1 i 2 oraz jedno wystąpienie klasy Foo, która jest współużytkowana między dwoma wątkami. jeśli wątek 1 wywoła a.method(), a następnie wątek 2 wywoła a.method(), wątek 2 zatrzyma się i poczeka do zwolnienia blokady mutex.

Wreszcie

class Foo { 
public: 
    void method(void) { 
     mutex.lock(); 
     cout << "method called"; 
     // long computation 
     mutex.unlock(); 
    } 

private: 
    static QMutex mutex; 
}; 

QMutex Foo::mutex; 

W tym przypadku, mutex jest zmienna statyczna klasa. Masz tylko jedną instancję muteksu dla każdej instancji obiektu. Załóżmy, że masz taką samą sytuację jak pierwszy przypadek powyżej: dwa wątki i dwa wystąpienia. W tym przypadku, gdy drugi wątek spróbuje wywołać b.method(), będzie musiał poczekać, aż a.method() zostanie zakończony przez pierwszy wątek, ponieważ blokada jest teraz unikatowa i współużytkowana przez wszystkie instancje klasy.

Aby uzyskać więcej informacji, Qt ma ładny tutorial wielowątkowość

https://doc.qt.io/qt-5/threads.html

+0

Przepraszam, zrobiłem naprawdę złą próbę pseudokodu. Naprawiłem to. Twoja odpowiedź na pierwszą część była dokładnie tym, o czym się zastanawiałem, dzięki! – int3

2

Twojego mutex jest instatiated lokalnie, na stos. Zatem wywołanie f() z jednego wątku będzie blokowało jego własną instancję muteksa. Każde inne wywołanie f() z innego wątku zostanie zablokowane. Tak więc warunki wyścigu mogą wystąpić z danymi uzyskanymi z g()! Nawet ciężko zadzwonić na to samo wystąpienie klasy:

MyClass foo; 
(In thread 1) foo->f(); 
(In thread 2) foo->f(); 

Jak lepiej obsługiwać zamek zależy od tego, co chcesz zrobić. Zgodnie z tym, co powiedziałeś, myślę, że lepszą zasadą byłoby bezpośrednia modyfikacja implementacji g(): musi ona blokować muteks zadeklarowany jako globalny na przykład lub jako statyczny w g(), który będzie współdzielony z każdym wywołaniem g(). O ile rozumiem, że chcesz zablokować swoje dane na całym świecie?

+0

, więc jeśli mam dane, które chcę zablokować w określonych instancjach Foo :: f(), czy powinienem utworzyć instancję mutex jako niestatyczne dane klasowe? – int3

+0

Cóż, chyba powinno wystarczyć. THEN wszelkie wywołania z instancji 'foo' z dowolnego wątku w moim przykładzie zapobiegną równoczesnemu dostępowi. (Ale nie z 2 różnych przypadków oczywiście) –