Pregunta ¿En qué situación usa un semáforo sobre un mutex en C ++?


A lo largo de los recursos que he leído sobre multithreading, mutex se usa y se discute más a menudo en comparación con un semáforo. Mi pregunta es cuándo usas un semáforo sobre un mutex? No veo semáforos en el hilo Boost. ¿Significa eso que los semáforos ya no usan mucho en estos días?

Por lo que he entendido, los semáforos permiten que varios hilos compartan un recurso. Esto solo es posible si esos hilos solo leen el recurso pero no lo escriben. ¿Es esto correcto?


32
2018-02-28 08:47


origen


Respuestas:


Boost.Thread tiene mutexes y variables de condición. Puramente en términos de funcionalidad, los semáforos son redundantes [*], aunque no sé si es por eso que se omiten.

Los semáforos son una primitiva más básica, más simple, y posiblemente implementada para ser más rápida, pero no tienen evitación de inversión de prioridad. Es discutible que sean más difíciles de usar que las variables de condición, ya que requieren el código del cliente para garantizar que el número de publicaciones "coincida" con el número de esperas de alguna manera adecuada. Con las variables de condición, es fácil tolerar publicaciones espurias, porque nadie realmente hace cualquier cosa sin verificar la condición.

Leer contra escribir recursos es una IMO arenque rojo, no tiene nada que ver con la diferencia entre un mutex y un semáforo. Si usa un semáforo de conteo, podría tener una situación en la que varios subprocesos accedan concurrentemente al mismo recurso, en cuyo caso, presumiblemente, tendría que ser de acceso de solo lectura. En esa situación, es posible que pueda usar shared_mutex de Boost.Thread en su lugar. Pero los semáforos no son "para" proteger recursos de la misma manera que los mutexes, sino que son "para" enviar una señal de un hilo a otro. Es posible utilizar ellos para controlar el acceso a un recurso.

Eso no significa que todos los usos de semáforos se relacionen con recursos de solo lectura. Por ejemplo, puede usar un semáforo binario para proteger un recurso de lectura / escritura. Sin embargo, puede que no sea una buena idea, ya que un mutex a menudo te ofrece un mejor comportamiento de programación.

[*] A continuación, se detalla cómo implementar un semáforo de conteo usando un mutex y una variable de condición. Para implementar un semáforo compartido, por supuesto, necesitas un mutex / condvar compartido:

struct sem {
    mutex m;
    condvar cv;
    unsigned int count;
};

sem_init(s, value)
    mutex_init(s.m);
    condvar_init(s.cv);
    count = value;

sem_wait(s)
    mutex_lock(s.m);
    while (s.count <= 0) {
        condvar_wait(s.cv, s.m);
    }
    --s.count;
    mutex_unlock(s.m);

sem_post(s)
    mutex_lock(s.m);
    ++s.count;
    condvar_broadcast(s.cv)
    mutex_unlock(s.m);

Por lo tanto, cualquier cosa que pueda hacer con semáforos, puede hacerlo con mutexes y variables de condición. No necesariamente mediante la implementación de un semáforo, sin embargo.


4
2018-02-28 15:32



El caso de uso típico para un mutex (que permite solo un acceso de subproceso a un recurso en cualquier momento) es mucho más común que los usos típicos de un semáforo. Pero un semáforo es en realidad el concepto más general: un mutex es (casi) un caso especial de un semáforo.

Las aplicaciones típicas serían: No desea crear más de (por ejemplo) 5 conexiones de base de datos. No importa cuántos subprocesos de trabajo haya, tienen que compartir estas 5 conexiones. O bien, si ejecuta en una máquina N-core, es posible que desee asegurarse de que ciertas tareas intensivas de CPU / memoria no se ejecuten en más de N subprocesos al mismo tiempo (porque eso solo reduciría el rendimiento debido a los conmutadores de contexto). y efectos de batido de caché). Incluso podría querer limitar el número de tareas intensivas paralelas de CPU / memoria a N-1, de modo que el resto del sistema no se muera de hambre. O imagine que una determinada tarea necesita mucha memoria, por lo que ejecutar más de N instancias de esa tarea al mismo tiempo generaría paginación. Puede usar un semáforo aquí para asegurarse de que no se ejecuten más de N instancias de esta tarea en particular al mismo tiempo.

EDIT / PS: De su pregunta "Esto solo es posible si esos hilos solo leen el recurso pero no lo escriben. ¿Es correcto?" y su comentario, me parece que está pensando en un recurso como una variable o un flujo, que puede leerse o escribirse y que solo se puede escribir por un hilo a la vez. No lo hagas Esto es engañoso en este contexto.

Piense en recursos como "agua". Puedes usar agua para lavar tus platos. Puedo usar agua para lavar mis platos al mismo tiempo. No necesitamos ningún tipo de sincronización para eso, porque hay suficiente agua para los dos. No usamos necesariamente lo mismo agua. (Y no puede "leer" o "escribir" agua). Pero el cantidad total de agua es finito. Entonces no es posible alguna número de partes para lavar sus platos al mismo tiempo. Este tipo de sincronización se realiza con un semáforo. Usualmente no solo con agua sino con otros recursos finitos como memoria, espacio en disco, rendimiento IO o núcleos de CPU.


18
2018-02-28 09:25



La esencia de la diferencia entre un mutex y un semáforo tiene que ver con el concepto de propiedad. Cuando se toma un mutex, pensamos en ese hilo como propietario del mutex y ese mismo hilo debe liberar posteriormente el mutex para liberar el recurso.

Para un semáforo, piense en tomar el semáforo como si consumiera el recurso, pero sin tomar posesión del mismo. Esto generalmente se conoce como que el semáforo está "vacío" en lugar de ser propiedad de un hilo. La característica del semáforo es que un hilo diferente puede "llenar" el semáforo nuevamente a su estado "completo".

Por lo tanto, los mutex se usan generalmente para la protección de concurrencia de los recursos (es decir: MUTual EXlusion) mientras que los semáforos se utilizan para la señalización entre hilos (como las señales de los semáforos que señalan entre los barcos). Un mutex en sí mismo no se puede usar para la señalización, pero los semáforos sí. Entonces, seleccionar uno sobre el otro depende de lo que estás tratando de hacer.

Ver otro una de mis respuestas aquí para una mayor discusión sobre un tema relacionado que cubre la distinción entre mutexes recursivos y no recursivos.


11
2018-02-28 17:12



Para controlar el acceso a un número limitado de recursos compartidos por varios hilos (ya sea en el proceso o dentro del proceso).

En nuestra aplicación, teníamos un recurso muy pesado y no queríamos asignar uno para cada uno de los hilos de trabajo M. Como un hilo de trabajo necesitaba el recurso solo para una pequeña parte de su trabajo, rara vez utilizábamos más de un par de recursos simultáneamente.

Entonces, asignamos N de esos recursos y los pusimos detrás de un semáforo inicializado a N. Cuando más de N subprocesos intentaban usar el recurso, simplemente bloqueaban hasta que uno estaba disponible.


9
2018-02-28 08:58



Siento que no hay una manera simple de DE VERDAD Responde tu pregunta sin ignorar información importante sobre semáforos. La gente ha escrito muchos libros sobre semáforos, por lo tanto, una respuesta de uno o dos párrafos es un perjuicio. Un libro popular es El pequeño libro de semáforos... para los que no les gustan los libros grandes :).

Aquí hay un decente artículo extenso que incluye MUCHOS detalles sobre cómo se utilizan los semáforos y cómo se deben usar.

Actualizar:
Dan señaló algunos errores en mis ejemplos, lo dejo con las referencias que ofrecen MUCHA mejor explicación que la mía :).

Aquí están las referencias que muestran las formas CORRECTAS de usar un semáforo:
1. Artículo de IBM
2. Clase de la Universidad de Chicago
3. El artículo de Netrino que originalmente publiqué.
4. El papel + código de "vender boletos". 


3
2018-02-28 09:40



Como tomado de Este artículo:

Un mutex permite que ocurra la sincronización entre procesos. Si instancia un mutex con un nombre (como en el código anterior), el mutex se convierte en todo el sistema. Esto es realmente útil si comparte la misma biblioteca entre muchas aplicaciones diferentes y necesita bloquear el acceso a una sección crítica de código que está accediendo a recursos que no se pueden compartir.

Finalmente, la clase de semáforo. Digamos que tiene un método que realmente consume mucha CPU y también utiliza recursos a los que necesita controlar el acceso (usando Mutexes :)). También ha determinado que un máximo de cinco llamadas al método es sobre todo lo que su máquina puede hacer sin que no responda. Su mejor solución aquí es hacer uso de la clase Semaphore que le permite limitar el acceso de un determinado número de subprocesos a un recurso.


1
2018-02-28 10:40



Por lo que yo entiendo, los semáforos son un término fuertemente relacionado con IPC estos días. Todavía significa una variable protegida que muchos procesos pueden modificar, pero entre los procesos y esta función es compatible con el sistema operativo.

Por lo general, no necesitamos una variable y un simple mutex cubre todos nuestros requisitos. Si aún necesitamos una variable, probablemente, la codifiquemos nosotros mismos - "variable + mutex" para obtener más control.

Curriculum vitae: no usamos semáforos en multihilo porque generalmente utilizamos mutex para simplificar y controlar, y utilizamos semáforos para IPC porque es compatible con el sistema operativo y es un nombre oficial para el mecanismo de sincronización de procesos.


0
2018-02-28 09:28



Semáforos fue concebido originalmente para la sincronización entre procesos. Windows usa WaitForMultipleObjects que es como un semáforo. En Linux World, la implementación inicial de pthread no permitía compartir un mutex en el proceso. Ahora lo hacen. El concepto de romper el incremento atómico (incremento entrelazado en Windows) junto con mutex ligero es la implementación más práctica en estos días después de que los hilos se convirtieran en la unidad de programación para la CPU. Si el incremento y el bloqueo estuvieran juntos (semáforo), el tiempo para adquirir / liberar bloqueos será demasiado largo y no podemos dividir esas 2 funciones de la unidad como lo hacemos hoy en día para el rendimiento y mejores construcciones de sincronización.


0
2018-01-20 17:22



Por lo que aprendí sobre semáforos y mutex en la universidad, los semáforos son más objetos teóricos, mientras que los mutex son una implementación de semáforos. Teniendo esto en cuenta, los semáforos son más flexibles.

Los Mutex son altamente dependientes de la implementación. Han sido optimizados para su propósito de bloqueo binario. El caso de uso normal de un mutex es un semáforo binario.

En general, al tratar de escribir código multiproceso sin errores, la simplicidad ayuda. Los Mutex se usan más porque su simplicidad ayuda a evitar complejos escenarios de interbloqueo que surgen del uso de semáforos.


-1
2018-02-28 10:51