O que é um mutex?

655

Um mutex é um conceito de programação usado com freqüência para resolver problemas com vários threads. Minha pergunta para a comunidade:

O que é um mutex e como você o usa?

bmurphy1976
fonte
2
Aqui está um bom artigo sobre a diferença: barrgroup.com/Embedded-Systems/How-To/RTOS-Mutex-Semaphore
Adam Davis
Um tutorial sobre mutex pode ajudar a esclarecer as coisas: stackoverflow.com/questions/4989451/mutex-example-tutorial
Nav
1
Um mutex é como uma chave de banheiro em um posto de gasolina, garantindo que apenas uma pessoa possa usar o banheiro de cada vez E que ninguém mais possa usar o banheiro até que o ocupante atual termine e a chave seja devolvida.
jonschlinkert

Respostas:

2154

Quando estou tendo uma grande discussão acalorada no trabalho, uso um frango de borracha que guardo na minha mesa para essas ocasiões. A pessoa que segura o frango é a única pessoa que pode falar. Se você não segurar o frango, não poderá falar. Você só pode indicar que deseja o frango e esperar até obtê-lo antes de falar. Depois de terminar de falar, você pode devolver o frango ao moderador, que o entregará à próxima pessoa a falar. Isso garante que as pessoas não falem umas com as outras e também tenham seu próprio espaço para conversar.

Substitua Chicken por Mutex e person por thread e você basicamente tem o conceito de mutex.

Claro, não existe um mutex de borracha. Apenas frango de borracha. Meus gatos já tiveram um rato de borracha, mas eles comeram.

Obviamente, antes de usar a galinha de borracha, você precisa se perguntar se você realmente precisa de 5 pessoas em uma sala e não seria mais fácil com uma pessoa na sala sozinha fazendo todo o trabalho. Na verdade, isso está apenas estendendo a analogia, mas você entendeu.

Xetius
fonte
9
Ótima resposta. Poderia ser argumentado que o Mutex é realmente as regras que impedem a passagem da galinha? e a galinha é a coisa que você está trancando?
SirYakalot
3
@ SirYakalot, você quer dizer que o frango é o recurso e o moderador é o mutex?
Owen
157
O frango é o mutex . As pessoas que usam o mu .. frango são tópicos concorrentes . O moderador é o sistema operacional . Quando as pessoas solicitam o frango, elas fazem um pedido de bloqueio. Quando você chama mutex.lock (), seu encadeamento trava no bloqueio () e faz uma solicitação de bloqueio ao sistema operacional. Quando o sistema operacional detecta que o mutex foi liberado de um encadeamento, ele simplesmente o fornece e lock () retorna - o mutex agora é seu e somente seu. Ninguém mais pode roubá-lo, porque chamar lock () o bloqueará. Também existe o try_lock () que bloqueará e retornará true quando o mutex for seu e imediatamente falso se o mutex estiver em uso.
21414
4
Você é um génio. Você também pode usar a metáfora da galinha de borracha para explicar os monitores?
Riccardo
98
Às vezes, a origem de alguns conceitos de programação não é clara. Um novato pode ficar se perguntando por que todo mundo está falando sobre regex. Não é aparente que regex seja a abreviação de [reg] ular [ex] expression. Da mesma forma, mutex é a abreviação de clusão [mut] ual [ex]. Isso pode facilitar a digestão do significado do termo. O @TheSmurf vinculou a ele em sua resposta, mas pode ser bom adicioná-lo aqui para fins históricos.
Dodzi Dzakuma
138

Um Mutex é uma bandeira mutuamente exclusiva. Ele atua como guardião de uma seção de código, permitindo um encadeamento e bloqueando o acesso a todos os outros. Isso garante que o código que está sendo controlado seja atingido apenas por um único thread por vez. Apenas certifique-se de liberar o mutex quando terminar. :)

Craig
fonte
11
Um mutex não tem nada a ver com uma seção de código em si, ele protege alguns recursos. Esse recurso pode ser um segmento de código se o mutex for usado apenas em torno desse código, mas, no minuto em que você começar a usá-lo em vários locais do código, sua explicação falhará. Normalmente, também pode ser usado para proteger alguma estrutura de dados, que pode ser acessada de vários locais do código.
precisa
73

Exclusão mútua. Aqui está a entrada da Wikipedia:

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

O objetivo de um mutex é sincronizar dois threads. Quando você tem dois encadeamentos tentando acessar um único recurso, o padrão geral é ter o primeiro bloco de código tentando acessar para configurar o mutex antes de inserir o código. Quando o segundo bloco de código tenta acessar, ele vê que o mutex está definido e aguarda até que o primeiro bloco de código esteja completo (e desmarca o mutex) e continua.

Detalhes específicos de como isso é feito obviamente variam muito de acordo com a linguagem de programação.

O Smurf
fonte
65

Quando você tem um aplicativo multithread, os diferentes threads às vezes compartilham um recurso comum, como uma variável ou similar. Essa fonte compartilhada geralmente não pode ser acessada ao mesmo tempo; portanto, é necessária uma construção para garantir que apenas um encadeamento esteja usando esse recurso por vez.

O conceito é chamado de "exclusão mútua" (breve Mutex) e é uma maneira de garantir que apenas um thread seja permitido dentro dessa área, usando esse recurso etc.

Como usá-los é específico do idioma, mas geralmente (se não sempre) se baseia em um mutex do sistema operacional.

Algumas linguagens não precisam dessa construção, devido ao paradigma, por exemplo, programação funcional (Haskell, ML são bons exemplos).

Mats Fredriksson
fonte
26

Em C #, o mutex comum usado é o Monitor . O tipo é ' System.Threading.Monitor '. Também pode ser usado implicitamente através da instrução ' lock (Object) '. Um exemplo de seu uso é ao construir uma classe 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;
    }
}

A instrução de bloqueio usando o objeto de bloqueio privado cria uma seção crítica. Exigindo que cada encadeamento aguarde até que o anterior seja concluído. O primeiro thread entrará na seção e inicializará a instância. O segundo thread aguardará, entrará na seção e obterá a instância inicializada.

Qualquer tipo de sincronização de um membro estático pode usar a instrução lock da mesma forma.

Anthony Mastrean
fonte
1
Esta é uma resposta dependente da implementação. Além disso, no CS, um monitor é diferente do mutex. Os monitores têm um mecanismo de sincronização, mas o mutex apenas trava a coisa até que não seja mais necessário. IDK sobre detalhes de implementação ou C # semântica, mas acho que o contexto da questão é mais ampla
marcoslhc
25

O que é um Mutex ?

O mutex (de fato, o termo mutex é abreviação de exclusão mútua), também conhecido como spinlock, é a ferramenta de sincronização mais simples usada para proteger regiões críticas e, assim, evitar condições de corrida. Ou seja, um encadeamento deve adquirir um bloqueio antes de entrar em uma seção crítica (na seção crítica, vários encadeamentos compartilham uma variável comum, atualizando uma tabela, gravando um arquivo etc.), ele libera o bloqueio quando sai da seção crítica.

O que é uma condição de corrida ?

Uma condição de corrida ocorre quando dois ou mais encadeamentos podem acessar dados compartilhados e eles tentam alterá-los ao mesmo tempo. Como o algoritmo de agendamento de encadeamentos pode alternar entre encadeamentos a qualquer momento, você não sabe a ordem em que os encadeamentos tentarão acessar os dados compartilhados. Portanto, o resultado da alteração nos dados depende do algoritmo de agendamento de threads, ou seja, os dois threads estão "correndo" para acessar / alterar os dados.

Exemplo da vida real:

Quando estou tendo uma grande discussão acalorada no trabalho, uso um frango de borracha que guardo na minha mesa para essas ocasiões. A pessoa que segura o frango é a única pessoa que pode falar. Se você não segurar o frango, não poderá falar. Você só pode indicar que deseja o frango e esperar até obtê-lo antes de falar. Depois de terminar de falar, você pode devolver o frango ao moderador, que o entregará à próxima pessoa a falar. Isso garante que as pessoas não falem umas com as outras e também tenham seu próprio espaço para conversar.

Substitua Chicken por Mutex e person por thread e você basicamente tem o conceito de mutex.

@Xetius

Uso em C #:

Este exemplo mostra como um objeto Mutex local é usado para sincronizar o acesso a um recurso protegido. Como cada segmento de chamada é bloqueado até adquirir a propriedade do mutex, ele deve chamar o método ReleaseMutex para liberar a propriedade do segmento.

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

Referência MSDN Mutex

habib
fonte
1
um exemplo muito bom
Siwei Shen
22

Há algumas ótimas respostas aqui, aqui está outra ótima analogia para explicar o que é mutex :

Considere banheiro único com uma chave . Quando alguém entra, eles pegam a chave e o banheiro está ocupado . Se alguém precisar usar o banheiro, precisará esperar na fila . Quando a pessoa no banheiro é feita , ela passa a chave para a próxima pessoa na fila. Faz sentido, certo?

Converta o banheiro da história em um recurso compartilhado e a chave em um mutex . Levar a chave ao banheiro (adquirir uma fechadura) permite que você a use. Se não houver uma chave (a trava está bloqueada), é preciso aguardar. Quando a chave é devolvida pela pessoa ( solte a trava ), você pode adquiri-la agora.

Chen A.
fonte
Mas o exemplo de c # não suporta sua asserção de fila, "passe a chave para a próxima pessoa na fila". O exemplo está demonstrando uma pilha ou aleatória. 1, 2 e 3 solicitam acesso, nessa sequência. Um é permitido primeiro na área protegida e três são permitidos. Uma fila teria dado ao segundo.
donvnielsen
Não estava me referindo a nenhuma implementação concreta ou linguagem de programação concreta. Meu exemplo diz respeito à abstração de alto nível do mutex como princípio.
Chen A.
18

Para entender o MUTEX primeiro, você precisa saber o que é "condição de corrida" e só então você entenderá por que o MUTEX é necessário. Suponha que você tenha um programa multithread e você tenha dois threads. Agora, você tem um trabalho na fila de trabalhos. O primeiro encadeamento verificará a fila de trabalhos e, após encontrar o trabalho, começará a executá-lo. O segundo encadeamento também verificará a fila de trabalhos e descobrirá que há um trabalho na fila. Portanto, também atribuirá o mesmo ponteiro de trabalho. Então, agora o que acontece, os dois threads estão executando o mesmo trabalho. Isso causará uma falha de segmentação. Este é o exemplo de uma condição de corrida.

A solução para esse problema é MUTEX. MUTEX é um tipo de bloqueio que bloqueia um segmento de cada vez. Se outro encadeamento quiser travá-lo, ele simplesmente será bloqueado.

Vale a pena ler o tópico MUTEX neste link do arquivo pdf .

user3751012
fonte
Por "tópico MUTEX", você quis dizer a seção sobre semáforos, porque seus exemplos são de semáforos binários, certo?
Carl G
2
bem um Mutex é apenas um semáforo com valor 1
marcoslhc
Qual é o nome do livro daquele capítulo que você compartilhou? Por favor
Omar Faroque Anik
@OmarFaroqueAnik, o livro mencionado é Advanced Linux Programming by CodeSourcery LLC, publicado pela New Riders Publishing e pode ser acessado na página inicial do domínio vinculado.
RMart #
11

As mutexes são úteis em situações em que você precisa impor acesso exclusivo a um recurso em vários processos, em que um bloqueio regular não ajuda, uma vez que funciona apenas em threads.

18hrs
fonte
Isso é mesmo verdade? Os processos individuais não criam sua própria cópia mutex?
Leon
0

Mutex: Mutex significa Mut ual Ex . Isso significa que por vez um processo / thread pode entrar na seção crítica. Na programação simultânea, em que vários threads / processos tentam atualizar o recurso compartilhado (qualquer variável, memória compartilhada etc.) podem levar a resultados inesperados. (Como o resultado depende de qual thread / processo obtém o primeiro acesso).

Para evitar um resultado inesperado, precisamos de algum mecanismo de sincronização, que garanta que apenas um encadeamento / processo obtenha acesso a esse recurso por vez.

A biblioteca pthread fornece suporte para o 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

Essa é a estrutura para o tipo de dados mutex, ou seja, pthread_mutex_t. Quando o mutex está bloqueado, __lock está definido como 1. Quando está desbloqueado, __lock está definido como 0.

Isso garante que dois processos / threads não possam acessar a seção crítica ao mesmo tempo.

Sandeep_black
fonte