Rodrigo Strauss :: Blog
Win32: sincronização parte 2
Preparando esse post eu percebi que o post de sincronização parte 1 não explica nada sobre sincronização, só apresenta o problema que a sincronização resolve. Estou parecendo aqueles vendedores de empresas de software que gastam mais tempo tentando provar que você tem um problema do que realmente mostrando a solução. Agora chegou a hora de mostrar a solução. :-)
Como vimos na primeira parte, o maior problema em programação multithread é o compartilhamento de dados entre as threads. Existem duas solução para isso. A primeira e mais usada é sincronizar o acesso aos dados, de forma que somente uma thread por vez manipule os dados em questão. Ou seja, a solução envolve "eliminar" (notou as aspas?) o multithread da parte que ele atrapalha. A segunda e pouco usada opção é eliminar o compartilhamento dos dados, usando filas, controle restrito de ownership e trabalhando com cópias do dados. Esse é o enfoque usado pelo Erlang, uma nova linguagem de programação.
A forma mais básica para sincronização em vários SOs é o critical section. Esse recurso permite definir um trecho de código que é executado por várias threads, mas que precisa ser sincronizado entre elas. Sincronizado == somente uma thread pode executar esse trecho de código por vez.
Revisando a nossa classe de lista ligada, vamos ver quais são os dados da classe:
template<typename T> class LinkedList { struct NODE { NODE* previous; NODE* next; T data; }; NODE rootNode_; unsigned int count_; ...
A princípio parece que precisamos sincronizar o rootNode_ e o count_. Apesar disso estar correto, não é simples assim, não esqueça que todos os nós apontados pelo rootNode_ são dados controlados pela classe, que também precisam ser sincronizados.
Olhando o fonte dessa classe, todos os métodos manipulam ou lêem essas duas variáveis. A forma mais fácil e óbvia seria cercar com um critical section o corpo de todas as funções da classe.
Em Win32, um critical section é definido pelo objeto (surpresa!) CRITICAL_SECTION. Esse objeto deve ser inicializado e terminado, dessa forma:
CRITICAL_SECTION cs; // // adivinha? // InitializeCriticalSection(&cs); // // aqui entramos no critical section. Quando uma thread está "dentro" de // um critical section, se alguma outra thread tentar entrar ficará // travada dentro da funcão EnterCriticalSection até que a primeira // thread chame LeaveCriticalSection. Isso garante que somente uma thread // executará esse trecho de código em um dado momento. // EnterCriticalSection(&cs); // // Saímos do critical section. Isso faz com que a próxima thread que estiver // esperando no ponto do EnterCriticalSection seja liberada. Note que somente // uma thread é liberada por vez. Caso mais de uma thread esteja esperando, elas // são liberadas na ordem que chegaram "na portinha" do critical section // LeaveCriticalSection(&cs); // // análogo ao CloseHandle // DeleteCriticalSection(&cs);
Resumindo: o Critical Section cria um "gargalo", onde só passa uma thread de cada vez. Ah, existe uma coisa interessante a ser acrescentada à explicação do post sobre threads: as threads permitem que um processo execute várias linhas de execução ao mesmo tempo, mas essas linhas de execução podem rodar o mesmo código, que está no mesmo lugar na memória. Ou seja, elas compartilham dados e código executável.
No próximo post iremos para o exemplo prático.
Em 30/01/2009 16:17, por Rodrigo Strauss





Primeiramente eu quero lhe agradecer pelo trabalho que você faz aqui.Ajuda muitas pesoas.Eu sei que a minha pergunta não tem muito a ver com a matéria acima mas lá vai.
Eu vi na matéria sobre flexibilidade que você escreveu, que você citou a linguagem Ocaml.Você tem interesse em aprender essa linguagem?E você tem idéia de quanto tempo é necessário para ficar bom em C++?
Desculpe a pergunta fora de hora.Espero que você possa me ajudar.
Obrigado