Hoje vou falar brevemente sobre como implementei um semáforo com base no objeto de sincronização "Evento".
Primeiro, percorra as definições.
1. O que é sincronização e por que é necessário?
Obviamente, podemos executar um conjunto de ações de várias maneiras. O mais simples - sequencialmente e em paralelo. A implementação paralela de certas ações pode ser alcançada executando vários threads. A idéia é simples: atribua algumas ações elementares (ou não) a cada thread e execute-as em uma determinada ordem. De um modo geral, podemos lançá-los todos ao mesmo tempo - é claro, obteremos um ganho de tempo. Isso é compreensível: uma coisa é produzir 10.000 palavras uma após a outra e outra é produzir simultaneamente, por exemplo, 100 palavras. Ganho de tempo de 100 vezes (mais ou menos, excluindo atrasos, etc.). Mas a tarefa original pode envolver uma sequência estrita de ações.
Por exemplo:
- Abrir arquivo
- Escreva texto em arquivo
- Fechar arquivo
O exemplo da estufa foi tomado especialmente (é claro que nenhum paralelismo é necessário aqui, tudo pode ser feito apenas sequencialmente), mas como uma tarefa de treinamento funcionará completamente e, o mais importante, a necessidade de execução consistente é claramente visível em seu exemplo. Ou aqui está outro exemplo, um pouco diferente:
- Gere três sequências de números aleatórios
- Exiba-os sequencialmente
Aqui, o primeiro ponto pode ser executado simultaneamente por três threads diferentes, mas o último, conclusão, deve ser feito sequencialmente e somente após o trabalho do primeiro ponto.
Em geral, as tarefas de paralelismo podem ser muito diferentes e é necessária alguma ferramenta para sincronizar threads.
2. Ferramentas para sincronização de threads
O Windows.h implementa várias ferramentas regulares de sincronização (os chamados "objetos de sincronização"). Entre os principais estão: região crítica, evento, mutex, semáforo. Sim, para o semáforo já existe uma implementação no windows.h. "Então, por que programá-lo?" Você pergunta. Bem, primeiro, para ter uma ideia melhor de como funciona. E, em segundo lugar, a prática extra do C ++ ainda não impediu ninguém :)
Como usaremos os Eventos, explicaremos o que é e como usá-lo.
Eventos são usados para notificar threads pendentes. Na verdade, isso é um sinal para o fluxo - ele pode ser acionado ou ainda não. Pelo próprio significado desse objeto, segue-se que ele possui algum estado de sinal e a capacidade de ajustá-lo (redefinir / “ligar”).
Assim, depois de conectar o windows.h, podemos criar um evento com:
HANDLE CreateEvent ( LPSECURITY_ATTRIBUTES lpEventAttributes,
Se a função for bem-sucedida, o identificador de evento será retornado. Se o objeto não puder ser criado, NULL será retornado.
Para alterar o status do evento para sinalizar, usamos a função:
BOOL SetEvent ( HANDLE hEvent
Se for bem-sucedido, retornará um valor diferente de zero.
Agora sobre o semáforo. O semáforo foi projetado para controlar o número de threads em execução simultaneamente. Suponha que tenhamos 1000 threads, mas apenas 2 possam funcionar por vez.Este é o tipo de ajuste que ocorre com a ajuda de um semáforo. E quais funções são implementadas para trabalhar com esse objeto de sincronização?
Para criar um semáforo, semelhante aos eventos:
HANDLE CreateSemaphore ( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
Na execução bem-sucedida, obtemos um ponteiro para o semáforo, na falha NULL.
O contador do semáforo está mudando constantemente (o encadeamento está em execução e um local vazio aparece); portanto, o estado do semáforo precisa ser alterado periodicamente. Isso é feito usando esta função:
BOOL ReleaseSemaphore ( HANDLE hSemaphore,
Se for bem-sucedido, o valor de retorno não é zero.
Também vale a pena prestar atenção à função:
DWORD WaitForSingleObject( HANDLE hHandle,
Dos valores retornados, estamos particularmente interessados em 2: WAIT_OBJECT_0 - significa que o estado do nosso objeto é sinal; WAIT_TIMEOUT - não esperamos pelo estado do sinal do objeto no tempo alocado.
3. A tarefa em si
No total, nossa tarefa é escrever nossos análogos em funções regulares. Não complicaremos muito a tarefa, faremos "implementação na primeira aproximação". O principal é preservar as características quantitativas do semáforo padrão. Código com comentários pode ser encontrado no
GitHub .
Devido à simplicidade da tarefa, não complicaremos particularmente o artigo, mas pode ser útil para alguém :)