Condição de corrida
Ao invés de fazer artigos longos, vou seguir as sugestões e postar pequenos trechos de código isolados (quem sabe um dia vira artigo). Hoje eu estava aqui pensando sobre programação paralela e fui brincar com o .NET. Segue um exemplo de condição de corrida.
Se eu executo um loop 100.000 vezes por 10 threads diferentes, cada uma incrementando um contador global em um, o que esperamos ao fim da execução é ver o valor de 1 milhão, certo? Só se você implementou corretamente sua aplicação multithreaded, com os devidos mecanismos de sincronização. Execute e compare os resultados.
Em anexo está o projeto (VS2008) para vocês brincarem e não vejo porque não compilar até no framework 1.1. Fazia um tempão que não brincava com threads, então em uma programação rápida, deve ter alguma coisa que melhorar.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;
namespace Threading
{
public class RaceCondition
{
int contadorGeral;
object ElementoSync = new object();
public void ComecaCorrida(Boolean SemControle)
{
contadorGeral = 0;
Thread[] ts = new Thread[10];
for (int i = 0; i < 10; i++)
{
// Cria threads e define qual método o delegate vai chamar, além de dar nome aos bois
ts[i] = new Thread(SemControle ? new ThreadStart(SemControleIncremento) : new ThreadStart(ComControleIncremento));
ts[i].Name = "Thread " + i.ToString();
Console.WriteLine(DumpThread(ts[i]));
}
foreach (Thread t in ts) t.Start();
foreach (Thread t in ts) t.Join();
}
private string DumpThread(Thread t)
{
return "ID Thread: " + t.ManagedThreadId.ToString() + " - " + t.Name;
}
private void SemControleIncremento()
{
int valorlocal = 0;
Random rnd = new Random();
for (int i = 0; i < 100000; i++)
{
valorlocal = contadorGeral;
// Estou usando o spinwait para não causar causar o bloqueio/escalonamento das threads o tempo todo (comportamento do Thread.sleep),
// então mantenho a thread rodando por x ciclos de CPU. O valor 500 é suficiente para não dar tempo da execução do loop
// acabar dentro do quantum alocado para a thread corrente.
Thread.SpinWait(rnd.Next(500));
//Thread.SpinWait(rnd.Next(50));
//Thread.Sleep(rnd.Next(5));
contadorGeral = valorlocal + 1;
}
Console.WriteLine(DumpThread(Thread.CurrentThread) + " " + contadorGeral.ToString());
}
private void ComControleIncremento()
{
int valorlocal = 0;
Random rnd = new Random();
// Sincroniza o acesso a esta região de código para evitar condição de corrida.
lock (ElementoSync)
{
for (int i = 0; i < 100000; i++)
{
valorlocal = contadorGeral;
Thread.SpinWait(rnd.Next(500));
//Thread.SpinWait(rnd.Next(50));
//Thread.Sleep(rnd.Next(5));
contadorGeral = valorlocal + 1;
}
}
Console.WriteLine(DumpThread(Thread.CurrentThread) + " " + contadorGeral.ToString());
}
}
}
static void Main(string[] args)
{
new RaceCondition().ComecaCorrida(true);
Console.WriteLine();
new RaceCondition().ComecaCorrida(false);
Console.ReadLine();
}
[]s
Luciano Caixeta Moreira
=============================================================
This posting is provided "AS IS" with no warranties, and confers no rights
=============================================================