Semáforo em Eventos C ++

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, //   BOOL bManualReset, //  : TRUE -  BOOL bInitialState, //  : TRUE -  LPCTSTR lpName //   ); 

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, //   LONG lInitialCount, //     LONG lMaximumCount, //    LPCTSTR lpName //   ); 

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, //    LONG lReleaseCount, //     LPLONG lpPreviousCount //   ); 

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, //   ,     DWORD dwMilliseconds //     ); 

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 :)

Source: https://habr.com/ru/post/pt476940/


All Articles