VB .NET - Threads - DeadLock - O Conceito, o problema e a resolução |
Neste
artigo vou apresentar o conceito de DeadLock, mostrar um exemplo e
também mostrar uma maneira de resolver o problema do DeadLock. Tudo isso usando a linguagem VB .NET. |
O que
é DeadLock ?
A tradução de deadlock pode ser : impasse, beco sem
saída
No contexto das Threads o DeadLock ocorre na seguinte situação:
Uma Thread, ou segmento ou linha de execução é uma forma de
um processo dividir a si mesmo em duas ou mais tarefas que podem ser
executadas concorrentemente. A linguagem C# suporta a execução paralela de código através do multithreading onde uma Thread é uma caminho de execução independente que esta apto para rodar simultaneamente com outras threads. Todas as classes relacionadas à segmentação(threading) estão presentes no namespace System.Threading |
Quando dois ou mais processos concorrentes (ou duas Threads) estão esperando pelo outro terminar e nenhum deles termina estamos em um impasse ou deadlock.
Cenário para ocorrência de um
DeadLock
Vamos supor que temos 2 threads:
a) Thread1
b) Thread2
E que temos dois recursos:
a) Recurso1
b) Recurso2
A Thread1 já adquiriu um bloqueio no Recurso1 e deseja adquirir um bloqueio no
Recurso2.
Ao mesmo tempo a Thread2 já adquiriu um bloqueio no Recurso2 e deseja adquirir
um bloqueio no Recurso1.
Uma thread fica esperando pela outra liberar o bloqueio e isso nunca acontece.
Temos
assim um impasse , um beco sem saída, temos um DeadLock.
Vou mostrar agora um exemplo prático mostrando uma simulação da ocorrência do deadlock e como resolver o problema.
Recursos usados:
Nota: Baixe e use a versão Community 2015 do VS ela é grátis e é equivalente a versão Professional.
Criando a solução no VS Community
Abra o VS Community 2015 e clique em New Project;
Selecione a linguagem Visual Basic e o template Console Application;
Informe o nome VBNET_DeadLock e clique no botão OK;
Definindo o código e simulando o DeadLock
Vamos definir no início do módulo duas variáveis que serão usadas para adquirir o bloqueio:
Private ObjLockA As Object = New Object()A seguir no método Main() inclua o código abaixo:
Sub Main()
Console.WriteLine("----Exemplo de DeadLock-----")
Console.WriteLine()
' Inicializa a thread com endereço Tarefa1
Dim thread1 As New Thread(AddressOf Tarefa1)
' Inicializa a thread com endereço Tarefa2
Dim thread2 As New Thread(AddressOf Tarefa2)
' Agenda a execução das threads
thread1.Start()
thread2.Start()
thread1.Join()
thread2.Join()
' Esta instrução nunca será executada
Console.WriteLine("Processamento concluído...")
Console.ReadKey()
End Sub
|
Neste código estamos criando duas Threads, thread1 e thread2 e executando o método Tarefa1 e Tarefa2 respectivamente em cada thread.
Vamos definir o código do método Tarefa1:
Private Sub Tarefa1()
SyncLock ObjLockA
Console.WriteLine("Tentando adquirir um bloqueio em ObjLockB")
' Pausa de 1 segundo
Thread.Sleep(1000)
SyncLock ObjLockB
' Este bloco nunca será executado
Console.WriteLine("Tarefa1 - Seção Crítica.")
' Acesso ao recurso compartilhado
End SyncLock
End SyncLock
End Sub
|
Neste código usamos o método SyncLock para adquirir um bloqueio exclusivo no objeto ObjLockA para um recurso antes de executar o recurso.
A seguir damos uma pausa de 1s na execução da thread usando o método Thread.Sleep(1000) e a seguir solicitamos a aquisição de um bloqueio no objeto ObjLockB
Vamos definir o código do método Tarefa2:
Private Sub Tarefa2()
SyncLock ObjLockB
Console.WriteLine("Tentando adquirir um bloqueio em ObjLockA")
SyncLock ObjLockA
' Este bloco de código nunca será executado
Console.WriteLine("Tarefa2 - Seção Crítica")
' Acesso ao recurso compartilhado
End SyncLock
End SyncLock
End Sub
|
Neste código fazemos o mesmo que no código anterior invertendo os objetos de bloqueio.
Neste cenário ao executarmos o projeto iremos obter o seguinte resultado:
Temos aqui o DeadLock.
Pois a thread1 esta esperando que a thread2 libere o bloqueio para o objeto ObjLockB e a thread2 esperando que a thread1 libere o bloqueio para o objeto ObjLockA.
O programa ficará 'congelado' indefinidamente esperando pela liberação dos bloqueios que nunca ocorrerá.
Resolvendo o problema do DeadLock
Para resolver o problema do DeadLock podemos :
1- Adquirir um bloqueio em um ordem específica;
2- Usar a classe Mutex (mútuo exclusivo);
3- Usar o método Monitor.TryEnter();
Neste exemplo vamos usar a classe Monitor.
A classe
Monitor provê um mecanismo que sincroniza o acesso aos objetos em um
programa multitarefa. Ela controla o acesso aos objetos através da concessão de
um bloqueio a um objeto para uma única thread.
O bloqueio do objeto fornece a capacidade de restringir o acesso a um bloco de
código conhecido como sessão crítica. Enquanto uma thread possuir o bloqueio
para o objeto nenhuma outra thread poderá adquirir o bloqueio.
Vamos usar o método Monitor.TryEnter que possui a seguinte sintaxe:
Monitor.TryEnter(object obj,
int milisegundos)
Tenta obter um bloqueio exclusivo para um objeto especificado por número
determinado de milisegundos.
Retorno:
true – se a thread adquiriu o bloqueio
false – se a thread não conseguir adquirir o bloqueio
Usamos os métodos Enter e Exit para marcar o início e o fim
de uma sessão crítica de código.
Se a sessão crítica for um conjunto de instrução contíguas então o bloqueio
adquirido pelo método Enter garante que somente uma única thread pode
executar o código.
Neste caso é recomendado colocar as instruções em um bloco try/catch e
usar o método Exit no bloco finally.
Vamos então alterar o código do método Tarefa2 conforme abaixo:
Private Sub Tarefa1()
SyncLock ObjLockA
Console.WriteLine("Tentando adquirir um bloqueio em ObjLockB")
' Pausa de 1 segundo
Thread.Sleep(1000)
' Tenta adquirir um bloqueio por 5 segundos
If Monitor.TryEnter(ObjLockB, 5000) Then
Try
' Este bloco nunca será executado
Console.WriteLine("Tarefa1 - Seção Crítica.")
' Acesso ao recurso compartilhado
Finally
Monitor.[Exit](ObjLockB)
End Try
Else
Console.WriteLine("Não foi possível adquirir um bloqueio, saindo de Tarefa1...")
End If
End SyncLock
End Sub
|
Agora usando o método Monitor.TryEnter(objLockB,5000) tentamos adquirir um bloqueio para o objeto ObjLockB por 5 segundos e se não for possível liberamos a tentativa de bloqueia usando o método Monitor.Exit(ObjLockB)
Executando o projeto novamente agora iremos obter o seguinte resultado:
E assim escapamos do DeadLock.
Pegue o projeto completo aqui: VBNET_DeadLock.zip
Até o próximo artigo...
(Disse
Jesus aos fariseus) Hipócritas, bem profetizou Isaías a vosso respeito,
dizendo:
Este povo se aproxima de mim com a sua boca e me honra com os seus lábios, mas o
seu coração está longe de mim.
Mas, em vão me adoram, ensinando doutrinas que são preceitos dos homens.
Mateus 15:7-9
Veja os
Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique
e confira !
Quer migrar para o VB .NET ?
Quer aprender C# ??
Quer aprender os conceitos da Programação Orientada a objetos ? Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ? Quer aprender a criar aplicações Web Dinâmicas usando a ASP .NET MVC 5 ? |
Gostou ? Compartilhe no Facebook Compartilhe no Twitter
Referências: