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?
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?
Odpowiedzi:
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ł.
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. :)
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.
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).
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.
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
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ć.
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.
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.