Semáforo en eventos C ++

Hoy hablaré brevemente sobre cómo implementé un semáforo basado en el objeto de sincronización "Evento".

Primero repase las definiciones.

1. ¿Qué es la sincronización y por qué es necesaria?


Obviamente, podemos realizar un conjunto de acciones de varias maneras. El más simple: secuencialmente y en paralelo. La implementación paralela de ciertas acciones se puede lograr ejecutando varios hilos. La idea es simple: asignamos a cada subproceso alguna acción elemental (o no) y la iniciamos en un cierto orden. En términos generales, podemos lanzarlos todos al mismo tiempo, por supuesto, obtendremos una ganancia a tiempo. Esto es comprensible: una cosa es generar 10,000 palabras una tras otra, y otra cosa es generar simultáneamente, por ejemplo, 100 palabras. Ganancia de tiempo de 100 veces (más o menos, excluyendo demoras, etc.). Pero la tarea original puede involucrar una secuencia estricta de acciones.

Por ejemplo:

  • Abrir archivo
  • Escribir texto para archivar
  • Cerrar archivo

El ejemplo de invernadero se tomó especialmente (está claro que no se necesita paralelismo aquí, todo se puede hacer simplemente de forma secuencial), pero como tarea de capacitación funcionará por completo, y lo más importante, la necesidad de una ejecución consistente es claramente visible en su ejemplo. O aquí hay otro ejemplo, ligeramente diferente:

  • Genera tres secuencias de números aleatorios.
  • Mostrarlos secuencialmente

Aquí, el primer punto puede realizarse simultáneamente por tres hilos diferentes, pero la última conclusión, debe hacerse secuencialmente, y solo después de resolver el primer punto.

En general, las tareas de paralelismo pueden ser muy diferentes y se necesita alguna herramienta para sincronizar hilos.

2. Herramientas para la sincronización de hilos


Windows.h implementa muchas herramientas de sincronización regulares (los llamados "objetos de sincronización"). Entre los principales están: región crítica, evento, mutex, semáforo. Sí, para semáforos ya hay una implementación en windows.h. "Entonces, ¿por qué programarlo?" Bueno, en primer lugar, para tener una mejor idea de cómo funciona. Y, en segundo lugar, la práctica adicional de C ++ aún no ha detenido a nadie :)

Dado que usaremos Eventos, explicaremos qué es y cómo usarlo.

Los eventos se utilizan para notificar subprocesos pendientes. Es decir, de hecho, esta es una señal para el flujo: puede activarse o no todavía. Del significado mismo de este objeto, se deduce que tiene algún estado de señal y la capacidad de ajustarlo (restablecer / "encender").

Entonces, después de conectar windows.h podemos crear un evento con:

HANDLE CreateEvent ( LPSECURITY_ATTRIBUTES lpEventAttributes, //   BOOL bManualReset, //  : TRUE -  BOOL bInitialState, //  : TRUE -  LPCTSTR lpName //   ); 

Si la función tiene éxito, se devolverá el identificador de evento. Si no se pudo crear el objeto, se devolverá NULL.

Para cambiar el estado del evento a señalizar, utilizamos la función:

 BOOL SetEvent ( HANDLE hEvent //   ); 

Si tiene éxito, devolverá un valor distinto de cero.

Ahora sobre el semáforo. El semáforo está diseñado para controlar el número de subprocesos que se ejecutan simultáneamente. Supongamos que tenemos 1000 hilos, pero solo 2 pueden funcionar a la vez. Este es el tipo de ajuste que ocurre con la ayuda de un semáforo. ¿Y qué funciones se implementan para trabajar con este objeto de sincronización?

Para crear un semáforo, similar a los eventos:

 HANDLE CreateSemaphore ( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, //   LONG lInitialCount, //     LONG lMaximumCount, //    LPCTSTR lpName //   ); 

Si tiene éxito, obtenemos un puntero al semáforo, si falla, obtenemos NULL.

El contador del semáforo cambia constantemente (el subproceso se está ejecutando y aparece un lugar vacío), por lo que el estado del semáforo debe cambiarse periódicamente. Esto se hace usando esta función:

 BOOL ReleaseSemaphore ( HANDLE hSemaphore, //    LONG lReleaseCount, //     LPLONG lpPreviousCount //   ); 

Si tiene éxito, el valor de retorno no es cero.

También vale la pena prestar atención a la función:

 DWORD WaitForSingleObject( HANDLE hHandle, //   ,     DWORD dwMilliseconds //     ); 

De los valores devueltos, estamos particularmente interesados ​​en 2: WAIT_OBJECT_0 - significa que el estado de nuestro objeto es señal; WAIT_TIMEOUT: no esperamos el estado de la señal del objeto en el tiempo asignado.

3. La tarea misma


En total, nuestra tarea es escribir nuestros análogos en funciones regulares. No complicaremos mucho la tarea, haremos "implementación en la primera aproximación". Lo principal es preservar las características cuantitativas del semáforo estándar. El código con comentarios se puede encontrar en GitHub .

Debido a la simplicidad de la tarea en sí, no complicaremos particularmente el artículo, pero puede ser útil para alguien :)

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


All Articles