Por: Roberto Alexis Farah

 

Olá pessoal! Estou em débito com meus leitores, pois já se passaram semanas e ainda não tive tempo de publicar um novo desafio do mês, ops..., digo, da semana.

No desafio de hoje começarei a abordar um tópico que ainda não foi parte dos desafios: problemas relacionados a multithreading.

Particularmente com Visual Basic .NET e C# é bastante fácil de se usar threads em aplicações, entretanto, sincronizar o acesso dessas threads a recursos compartilhados é outra história.

Muitas vezes quando trabalhando em incidentes de aplicações com multithread e sintomas usualmente classificados como hang ou performance, recebo perguntas de clientes como:

 

-      Devo fazer o lock menos ou mais granular?

-      Qual o melhor mecanismo de sincronização para usar em determinada situação?

-      Porque minha aplicação roda com boa performance usando 25 threads, roda com mais performance com 50 threads mas roda lentamente com 100 threads?

-      Porque minha aplicação multithread degrada a performance lentamente?

-      Quando preciso e quando não preciso me preocupar com acesso sincronizado de múltiplas threads?

 

Portanto, começo por esse desafio a escrever sobre multithreading, continuando a contribuição para se evitar o uso de POG (Programação Orientada a Gambiarras) (isso é bem engraçado J )em qualquer contexto, principalmente em programação multithreading.

 

 

CENÁRIO

 

Temos uma aplicação C# abaixo, feita com .NET Framework 2.0 que pode ser facilmente convertida para VB.NET e o mesmo sintoma ocorrerá.

A aplicação usa um método em modo compartilhado que incrementa uma variável, entretanto, assuma que a operação poderia ser outra coisa diferente de incrementar uma variável.

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

 

namespace ThreadBug

{

          public class Bacteria

          {

                   private static int _bacteriaCount;

                  

                   private static void DoSomething()

                   {

                             Console.WriteLine("DoSomething chamado por {0} no momento {1}",

                                                                      Thread.CurrentThread.ManagedThreadId,

                                                                     DateTime.Now.ToLongTimeString());

 

                             // Assuma que o método poderia fazer outra operação que não incrementar uma variável!                                                                     

                             _bacteriaCount++; 

                            

                             Thread.Sleep(3000);  

                   }

                  

                   public Bacteria()

                   {

                             lock(this)

                             {

                                      DoSomething();

                             }

                   }

          }

         

          public class SpecializedBacteria : Bacteria

          {

          }

         

         

          class Program

          {

                   private static void Worker1()

                   {

                             Console.WriteLine("Worker1 executando thread {0}",

                                                                     Thread.CurrentThread.ManagedThreadId);

                                                                            

                             Bacteria bac = new Bacteria();

                   }

 

                   private static void Worker2()

                   {

                             Console.WriteLine("Worker2 executando thread {0}",

                                                                     Thread.CurrentThread.ManagedThreadId);

 

                             SpecializedBacteria bac = new SpecializedBacteria();

                   }

                  

                   [STAThread]

                   static void Main(string[] args)

                   {

                             Thread thread1 = new Thread(new ThreadStart(Worker1));

                             Thread thread2 = new Thread(new ThreadStart(Worker2));

                            

                             Console.WriteLine("Iniciando as threads em {0}",

                                                       DateTime.Now.ToLongTimeString());

                             thread1.Start();

                             thread2.Start();

                            

                             Thread.Sleep(4000);

                   }

          }

}

 

 

SINTOMA

 

As threads acessam, ao mesmo tempo, o método DoSomething(). O correto é que apenas uma thread, em determinado momento, acessar o método estático.

 

OBJETIVO

 

Identifique o PROBLEMA ocasionando a falha na sincronização das threads e proponha uma SOLUÇÃO.

 

Nota: Assuma que o método poderia fazer uma operação diferente de incrementar uma variável, portanto, a solução deve contemplar essa possibilidade.