¿Cómo elijo entre Semaphore y SemaphoreSlim?


109

Sus interfaces públicas parecen similares. los documentación indica que SemaphoreSlim es una alternativa ligera y no usa semáforos del Kernel de Windows. Este recurso afirma que SemaphoreSlim es mucho más rápido. ¿En qué situaciones el SemaphoreSlim tiene más sentido que el Semaphore y viceversa?


1
Semaphore siempre pasa el trabajo al sistema operativo. Eso lo hace relativamente caro si el semáforo no se disputa fuertemente, la llamada del núcleo cuesta fácilmente 400 nanosegundos. El favor delgado primero intenta hacerlo de forma económica con una variable compartida, solo llama al sistema operativo cuando eso no funcionó. Siempre le gusta lo barato, excepto en el caso de la esquina donde el semáforo debe ser compartido por múltiples procesos.
Hans Passant

Respuestas:


64

Una diferencia es que SemaphoreSlim no permite semáforos con nombre, que pueden ser de todo el sistema. Esto significaría que un SemaphoreSlim no se podría utilizar para la sincronización entre procesos.

La documentación de MSDN también indica que SemSlim debe utilizarse cuando "se espera que los tiempos de espera sean muy cortos". Eso generalmente encajaría bien con la idea de que la versión delgada es más liviana para la mayoría de las compensaciones.


66
Ojalá MS hubiera escrito algo sobre lo que sucede cuando los tiempos de espera no son "muy cortos".
John Reynolds

79
... o lo que significa "muy corto"
Richard Szalay

9
Dependiendo del sistema, 1 microsegundo para Semaphore y 1/4 microsegundo para SemaphoreSlim para hacer un WaitOne o Release ["C # 5.0 in a Nutshell" pág. 890] Entonces, ¿quizás eso es lo que quieren decir con tiempos de espera muy cortos?
David Sherret

2
@dhsto ¡Buena referencia! Pero supongo que el artículo vinculado significa "cuando los tiempos de espera para acceder al recurso compartido son muy cortos", es decir, el recurso no permanecerá en uso exclusivo durante largos períodos de tiempo, en lugar de significar los tiempos de espera para el código del semáforo en sí. El código del semáforo siempre se ejecutará independientemente de cuánto tiempo se mantenga bloqueado el recurso.
culix

4
@culix Mirando la fuente de Semaphore versus SemaphoreSlim, sospecho que la razón principal por la que SemaphoreSlim solo debería usarse para esperas "muy cortas" es porque usa una espera giratoria. Esto es más sensible, pero consume mucha CPU y sería un desperdicio para períodos de espera más largos donde las respuestas instantáneas son menos importantes. El semáforo bloquea el hilo en su lugar.
r2_118

17

La documentación de MSDN describe la diferencia.

En una frase:

  • La clase SemaphoreSlim representa un semáforo ligero y rápido que se puede usar para esperar dentro de un solo proceso cuando se espera que los tiempos de espera sean muy cortos.

1
Para agregar a esto, use SemaphoreSlim en todos los casos en los que su aplicación sea el único proceso en su computadora que necesitará acceder a ese Semaphore.
Austin Salgat

2
@Salgat ¿Por qué harías eso? Como se describe en otras respuestas, SemaphoreSlimse implementa usando SpinWait, por lo que si espera mucho, perderá mucho tiempo de CPU. Lo cual ni siquiera es una buena idea si su proceso es el único proceso en la computadora.
M.Stramm

De la documentación de MSDN, "La clase SemaphoreSlim es el semáforo recomendado para la sincronización dentro de una sola aplicación". Excepto en casos especiales, debe usar SemaphoreSlim de forma predeterminada si solo un proceso usa ese semáforo. Generalmente existen excepciones especiales para cualquier tipo de datos concurrentes, lo cual es evidente.
Austin Salgat

17

SemaphoreSlim se basa en SpinWait y Monitor, por lo que el hilo que espera adquirir el bloqueo está quemando ciclos de CPU durante algún tiempo con la esperanza de adquirir el bloqueo antes de ceder a otro hilo. Si eso no sucede, los subprocesos permiten que los sistemas cambien de contexto e intentan nuevamente (quemando algunos ciclos de CPU) una vez que el sistema operativo programa ese subproceso nuevamente. Con esperas largas, este patrón puede consumir una cantidad sustancial de ciclos de CPU. Entonces, el mejor escenario para dicha implementación es cuando la mayoría de las veces no hay tiempo de espera y puede adquirir el bloqueo casi instantáneamente.

Semaphore se basa en la implementación en el kernel del sistema operativo, por lo que cada vez que adquiere el bloqueo, gasta bastantes ciclos de CPU, pero después de eso, el hilo simplemente duerme el tiempo que sea necesario para obtener el bloqueo.


12

Con respecto a la controversia sobre "tiempos cortos":

Al menos la documentación de SemaphoreSlim MSDN establece que

La clase SemaphoreSlim es el semáforo recomendado para la sincronización dentro de una sola aplicación.

en la sección de Comentarios. La misma sección también indica la principal diferencia entre Semaphore y SemaphoreSlim:

SemaphoreSlim es una alternativa ligera a la clase Semaphore que no usa semáforos del kernel de Windows. A diferencia de la clase Semaphore, la clase SemaphoreSlim no admite semáforos de sistema con nombre. Puede usarlo solo como semáforo local.


1
Más detalles: [SemaphoreSlim and other locks] use busy spinning for brief periods before they put the thread into a true Wait state. When wait times are expected to be very short, spinning is far less computationally expensive than waiting, which involves an expensive kernel transition: dotnet.github.io/docs/essentials/collections/...
Marco Sila

0

Miré el código fuente aquí y esto es lo que se me ocurrió:

  1. Tanto Semaphore como SemaphoreSlim se derivan de WaitHandle, que utiliza internamente el identificador nativo de Win32. Es por eso que necesita Dispose () both. Así que la idea de que Slim es ligero es sospechosa.

  2. SemaphoreSlim usa SpinWait internamente, mientras que Semaphore no. Eso me dice que en los casos en que se espera que la espera sea larga, Semaphore debería funcionar mejor al menos en el sentido de que no ahogará su CPU.


2
SemaphoreSlim no se deriva de WaitHandle. En realidad, se creó con un único objetivo: eliminar la derivación de WaitHandle, que es el espacio del kernel primitivo, en tareas en las que necesita sincronizar operaciones dentro de un solo proceso.
Igor V Savchenko
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.