Co to jest mutex?

652

Mutex to koncepcja programowania, która jest często używana do rozwiązywania problemów związanych z wielowątkowością. Moje pytanie do społeczności:

Co to jest mutex i jak go używasz?

bmurphy1976
źródło
2
Oto dobry artykuł na temat różnicy: barrgroup.com/Embedded-Systems/How-To/RTOS-Mutex-Semaphore
Adam Davis
Samouczek na temat mutex może pomóc w uporządkowaniu sytuacji: stackoverflow.com/questions/4989451/mutex-example-tutorial
Nav
1
Mutex jest jak klucz do łazienki na stacji benzynowej, zapewniając, że tylko jedna osoba może korzystać z łazienki na raz ORAZ nikt inny nie może korzystać z toalety, dopóki aktualny mieszkaniec nie skończy i nie zwróci klucza.
jonschlinkert

Odpowiedzi:

2150

Kiedy prowadzę dużą gorącą dyskusję w pracy, używam gumowego kurczaka, który trzymam na biurku na takie okazje. Osoba trzymająca kurczaka jest jedyną osobą, która może rozmawiać. Jeśli nie trzymasz kurczaka, nie możesz mówić. Możesz tylko wskazać, że chcesz kurczaka i poczekać, aż go zdobędziesz, zanim zaczniesz mówić. Kiedy skończysz mówić, możesz oddać kurczaka z powrotem moderatorowi, który przekaże go następnej osobie, która zabierze głos. Zapewnia to, że ludzie nie rozmawiają ze sobą, a także mają własną przestrzeń do rozmowy.

Zamień kurczaka na Mutex, a osobę na nić, a właściwie masz pojęcie mutex.

Oczywiście nie ma czegoś takiego jak gumowy muteks. Tylko gumowy kurczak. Moje koty miały kiedyś gumową mysz, ale zjadły ją.

Oczywiście, zanim użyjesz gumowego kurczaka, musisz zadać sobie pytanie, czy rzeczywiście potrzebujesz 5 osób w jednym pokoju i czy nie byłoby łatwiej, gdyby jedna osoba w pokoju sama wykonała całą pracę. W rzeczywistości jest to tylko rozszerzenie analogii, ale masz pomysł.

Xetius
źródło
9
Świetna odpowiedź. Czy można argumentować, że Mutex to tak naprawdę zasady, które powstrzymują przekazywanie kurczaka? a kurczak jest tym, co blokujesz?
SirYakalot
3
@ SirYakalot, masz na myśli, że kurczak jest zasobem, a moderatorem jest muteks?
Owen,
155
Kurczak to muteks . Ludzie gromadzący mu .. kurczaka są konkurującymi wątkami . Moderatorem jest system operacyjny . Gdy ludzie żądają kurczaka, robią prośbę o zablokowanie. Po wywołaniu mutex.lock () wątek zatrzymuje się w lock () i wysyła żądanie blokady do systemu operacyjnego. Kiedy system operacyjny wykryje, że muteks został zwolniony z wątku, po prostu daje go tobie, a lock () powraca - muteks jest teraz twój i tylko twój. Nikt inny nie może go ukraść, ponieważ wywołanie blokady () go zablokuje. Istnieje również try_lock (), który blokuje i zwraca true, gdy mutex jest twój, i natychmiast false, jeśli mutex jest w użyciu.
Петър Петров
4
Jesteś geniuszem. Czy możesz również użyć gumowej metafory kurczaka, aby wyjaśnić monitory?
Riccardo,
98
Czasami pochodzenie niektórych koncepcji programistycznych jest niejasne. Początkujący może zastanawiać się, dlaczego wszyscy mówią o wyrażeniach regularnych. Nie jest oczywiste, że wyrażenie regularne jest skrótem od [reg] ular [ex] pression. Podobnie mutex jest skrótem od [mut] ula [ex] clusion. Może to ułatwić zrozumienie znaczenia tego terminu. @ TheSmurf podłączył się do tego w swojej odpowiedzi, ale dobrze byłoby dodać go tutaj do celów historycznych.
Dodzi Dzakuma,
137

Mutex to wzajemnie wykluczająca się flaga. Działa jako bramka dla sekcji kodu, umożliwiając jeden wątek i blokując dostęp do wszystkich innych. Zapewnia to, że kontrolowany kod będzie trafiany tylko przez jeden wątek na raz. Pamiętaj, aby po zakończeniu wypuścić mutex. :)

Craig
źródło
10
Muteks nie ma nic wspólnego z sekcją kodu per se, chroni on niektóre zasoby. Ten zasób może być segmentem kodu, jeśli muteks jest używany tylko wokół tego kodu, ale gdy tylko zaczniesz używać muteksu w wielu miejscach w kodzie, twoje wyjaśnienie nie powiedzie się. Zazwyczaj można go również wykorzystać do ochrony niektórych struktur danych, do których można uzyskać dostęp z wielu miejsc w kodzie.
paxdiablo
72

Wzajemne wykluczenie. Oto wpis w Wikipedii:

http://en.wikipedia.org/wiki/Mutual_exclusion

Celem muteksu jest synchronizacja dwóch wątków. Gdy masz dwa wątki próbujące uzyskać dostęp do jednego zasobu, ogólny wzorzec jest taki, że pierwszy blok kodu próbuje uzyskać dostęp do ustawienia muteksu przed wprowadzeniem kodu. Gdy drugi blok kodu próbuje uzyskać dostęp, widzi, że muteks jest ustawiony i czeka na ukończenie pierwszego bloku kodu (i odznacza muteks), a następnie kontynuuje.

Konkretne szczegóły tego, jak można to osiągnąć, różnią się znacznie w zależności od języka programowania.

Smerf
źródło
65

W przypadku aplikacji wielowątkowej różne wątki czasami dzielą wspólny zasób, taki jak zmienna lub podobny. Często nie można uzyskać dostępu do tego wspólnego źródła, dlatego potrzebna jest konstrukcja, aby zapewnić, że tylko jeden wątek korzysta z tego zasobu na raz.

Pojęcie to nazywa się „wzajemnym wykluczeniem” (krótki Mutex) i jest sposobem na zapewnienie, że tylko jeden wątek jest dozwolony w tym obszarze, przy użyciu tego zasobu itp.

Sposób ich użycia zależy od języka, ale często (jeśli nie zawsze) jest oparty na muteksie systemu operacyjnego.

Niektóre języki nie potrzebują tej konstrukcji ze względu na paradygmat, na przykład programowanie funkcjonalne (dobre przykłady to Haskell, ML).

Mats Fredriksson
źródło
26

W języku C # najczęściej stosowanym muteksem jest monitor . Typ to „ System.Threading.Monitor ”. Można go również użyć niejawnie za pomocą instrukcji „ lock (Object) ”. Jednym z przykładów jego zastosowania jest budowanie klasy Singleton.

private static readonly Object instanceLock = new Object();
private static MySingleton instance;
public static MySingleton Instance
{
    lock(instanceLock)
    {
        if(instance == null)
        {
            instance = new MySingleton();
        }
        return instance;
    }
}

Instrukcja lock przy użyciu prywatnego obiektu blokady tworzy sekcję krytyczną. Wymaganie od każdego wątku oczekiwania na zakończenie poprzedniego. Pierwszy wątek przejdzie do sekcji i zainicjuje instancję. Drugi wątek zaczeka, przejdzie do sekcji i otrzyma zainicjowaną instancję.

Każda synchronizacja elementu statycznego może korzystać z instrukcji lock podobnie.

Anthony Mastrean
źródło
1
Jest to odpowiedź zależna od implementacji. Ponadto w CS monitor różni się od muteksu. Monitory mają mechanizm synchronizacji, ale mutex po prostu blokuje to urządzenie, aż nie będzie już potrzebne. IDK na temat szczegółów implementacji lub semantyki C #, ale myślę, że kontekst pytania jest szerszy
marcoslhc
24

Co to jest Mutex ?

Muteks (w rzeczywistości termin mutex jest skrótem od wzajemnego wykluczenia), znany również jako spinlock, jest najprostszym narzędziem synchronizacji, które służy do ochrony krytycznych regionów, a tym samym do zapobiegania warunkom wyścigowym. Oznacza to, że wątek musi uzyskać blokadę przed wejściem do sekcji krytycznej (w sekcji krytycznej wiele wątków ma wspólną zmienną, aktualizuje tabelę, zapisuje plik i tak dalej), zwalnia blokadę, gdy opuszcza sekcję krytyczną.

Co to jest stan wyścigu ?

Wyścig występuje, gdy dwa lub więcej wątków może uzyskać dostęp do współużytkowanych danych i próbują je zmienić w tym samym czasie. Ponieważ algorytm planowania wątków może w dowolnym momencie przełączać się między wątkami, nie znasz kolejności, w której wątki będą próbowały uzyskać dostęp do udostępnionych danych. Dlatego wynik zmiany danych zależy od algorytmu szeregowania wątków, tzn. Oba wątki „ścigają się” w celu uzyskania dostępu / zmiany danych.

Przykład z życia:

Kiedy prowadzę dużą gorącą dyskusję w pracy, używam gumowego kurczaka, który trzymam na biurku na takie okazje. Osoba trzymająca kurczaka jest jedyną osobą, która może rozmawiać. Jeśli nie trzymasz kurczaka, nie możesz mówić. Możesz tylko wskazać, że chcesz kurczaka i poczekać, aż go zdobędziesz, zanim zaczniesz mówić. Kiedy skończysz mówić, możesz oddać kurczaka z powrotem moderatorowi, który przekaże go następnej osobie, która zabierze głos. Zapewnia to, że ludzie nie rozmawiają ze sobą, a także mają własną przestrzeń do rozmowy.

Zamień kurczaka na Mutex, a osobę na nić, a właściwie masz pojęcie mutex.

@Xetius

Zastosowanie w C #:

Ten przykład pokazuje, jak lokalny obiekt Mutex jest używany do synchronizowania dostępu do chronionego zasobu. Ponieważ każdy wywołujący wątek jest blokowany, dopóki nie uzyska prawa własności do muteksu, musi wywołać metodę ReleaseMutex, aby zwolnić własność wątku.

using System;
using System.Threading;

class Example
{
    // Create a new Mutex. The creating thread does not own the mutex.
    private static Mutex mut = new Mutex();
    private const int numIterations = 1;
    private const int numThreads = 3;

    static void Main()
    {
        // Create the threads that will use the protected resource.
        for(int i = 0; i < numThreads; i++)
        {
            Thread newThread = new Thread(new ThreadStart(ThreadProc));
            newThread.Name = String.Format("Thread{0}", i + 1);
            newThread.Start();
        }

        // The main thread exits, but the application continues to
        // run until all foreground threads have exited.
    }

    private static void ThreadProc()
    {
        for(int i = 0; i < numIterations; i++)
        {
            UseResource();
        }
    }

    // This method represents a resource that must be synchronized
    // so that only one thread at a time can enter.
    private static void UseResource()
    {
        // Wait until it is safe to enter.
        Console.WriteLine("{0} is requesting the mutex", 
                          Thread.CurrentThread.Name);
        mut.WaitOne();

        Console.WriteLine("{0} has entered the protected area", 
                          Thread.CurrentThread.Name);

        // Place code to access non-reentrant resources here.

        // Simulate some work.
        Thread.Sleep(500);

        Console.WriteLine("{0} is leaving the protected area", 
            Thread.CurrentThread.Name);

        // Release the Mutex.
        mut.ReleaseMutex();
        Console.WriteLine("{0} has released the mutex", 
            Thread.CurrentThread.Name);
    }
}
// The example displays output like the following:
//       Thread1 is requesting the mutex
//       Thread2 is requesting the mutex
//       Thread1 has entered the protected area
//       Thread3 is requesting the mutex
//       Thread1 is leaving the protected area
//       Thread1 has released the mutex
//       Thread3 has entered the protected area
//       Thread3 is leaving the protected area
//       Thread3 has released the mutex
//       Thread2 has entered the protected area
//       Thread2 is leaving the protected area
//       Thread2 has released the mutex

MSDN Reference Mutex

habib
źródło
1
bardzo dobry przykład
Siwei Shen
22

Jest tu kilka świetnych odpowiedzi, oto kolejna wielka analogia do wyjaśnienia, czym jest muteks :

Rozważ pojedynczą toaletę z kluczem . Kiedy ktoś wchodzi, bierze klucz i toaleta jest zajęta . Jeśli ktoś musi skorzystać z toalety, musi poczekać w kolejce . Kiedy osoba w toalecie jest skończona , przekazuje klucz kolejnej osobie w kolejce. Ma sens, prawda?

Przekształć toaletę w historii w udostępniony zasób i klucz do muteksu . Zabranie klucza do toalety (zdobycie zamka) pozwala z niego skorzystać. Jeśli nie ma klucza (blokada jest zablokowana), musisz poczekać. Gdy osoba zwróci klucz ( zwolnij blokadę ), możesz go teraz zdobyć.

Chen A.
źródło
Ale przykład c # nie obsługuje twojego potwierdzenia w kolejce: „przekaż klucz kolejnej osobie w kolejce”. Przykładem jest demonstracja stosu lub losowości. 1, 2 i 3 wszyscy żądają dostępu w tej kolejności. Jeden jest wpuszczany najpierw do obszaru chronionego, a następnie trzy są dozwolone. Kolejka dałaby to drugiemu.
donvnielsen,
Nie mówiłem o żadnej konkretnej implementacji ani konkretnym języku programowania. Mój przykład dotyczy zasadniczo abstrakcyjnej mutacji muteksu.
Chen A.
18

Aby zrozumieć MUTEX, najpierw musisz wiedzieć, co to jest „warunek wyścigu”, a dopiero potem zrozumiesz, dlaczego MUTEX jest potrzebny. Załóżmy, że masz program wielowątkowy i masz dwa wątki. Teraz masz jedno zadanie w kolejce zadań. Pierwszy wątek sprawdzi kolejkę zadań i po znalezieniu zadania zacznie go wykonywać. Drugi wątek sprawdzi także kolejkę zadań i stwierdzi, że w kolejce jest jedno zadanie. Przydzieli również ten sam wskaźnik zadania. Tak więc teraz, co się dzieje, oba wątki wykonują to samo zadanie. Spowoduje to błąd segmentacji. To jest przykład warunków wyścigu.

Rozwiązaniem tego problemu jest MUTEX. MUTEX jest rodzajem zamka, który blokuje jeden wątek na raz. Jeśli inny wątek chce go zablokować, wątek zostaje po prostu zablokowany.

Warto przeczytać temat MUTEX w tym linku do pliku pdf .

użytkownik3751012
źródło
Przez „temat MUTEX” miałeś na myśli rozdział dotyczący semaforów, ponieważ jego przykłady dotyczą semaforów binarnych, prawda?
Carl G,
2
cóż, Mutex to tylko semafor o wartości 1
marcoslhc
Jak nazywa się książka tego rozdziału, który udostępniłeś? Proszę
Omar Faroque Anik
@OmarFaroqueAnik wspomniana książka to Advanced Linux Programming autorstwa CodeSourcery LLC, opublikowana przez New Riders Publishing i można uzyskać do niej dostęp ze strony głównej połączonej domeny.
RMart
11

Muteksy są przydatne w sytuacjach, w których musisz wymusić wyłączny dostęp do zasobu w wielu procesach, gdzie zwykła blokada nie pomoże, ponieważ działa tylko w wątkach.

18 godz
źródło
Czy to naprawdę prawda? Czy poszczególne procesy nie stworzą własnej kopii muteksu?
Leon
0

Mutex: mutex oznacza Mut seksualnego Ex sku. Oznacza to, że jeden proces / wątek może wejść do sekcji krytycznej. W programowaniu współbieżnym, w którym wiele wątków / procesów próbuje zaktualizować współużytkowany zasób (dowolną zmienną, pamięć współdzieloną itp.), Może dojść do nieoczekiwanego wyniku. (Ponieważ wynik zależy od tego, który wątek / proces uzyskuje pierwszy dostęp).

Aby uniknąć takiego nieoczekiwanego wyniku, potrzebujemy mechanizmu synchronizacji, który zapewnia, że ​​tylko jeden wątek / proces uzyskuje dostęp do takiego zasobu na raz.

Biblioteka pthread zapewnia obsługę Mutex.

typedef union
{
  struct __pthread_mutex_s
  {
    ***int __lock;***
    unsigned int __count;
    int __owner;
#ifdef __x86_64__
    unsigned int __nusers;
#endif
 int __kind;
#ifdef __x86_64__
    short __spins;
    short __elision;
    __pthread_list_t __list;
# define __PTHREAD_MUTEX_HAVE_PREV      1
# define __PTHREAD_SPINS             0, 0
#else
    unsigned int __nusers;
    __extension__ union
    {
      struct
      {
        short __espins;
        short __elision;
# define __spins __elision_data.__espins
# define __elision __elision_data.__elision
# define __PTHREAD_SPINS         { 0, 0 }
      } __elision_data;
      __pthread_slist_t __list;
    };
#endif

Jest to struktura typu danych mutex, tj. Pthread_mutex_t. Gdy mutex jest zablokowany, __lock ustawiony na 1. Gdy jest odblokowany __lock ustawiony na 0.

Zapewnia to, że żadne dwa procesy / wątki nie mogą uzyskać dostępu do sekcji krytycznej w tym samym czasie.

Sandeep_black
źródło