2011-07-23 12 views
11

Necesito dos hilos para avanzar en un patrón "tic tock". Cuando implmented con un semáforo esto se ve bien:¿Usar un mutex como semáforo?

Semaphore tick_sem(1); 
Semaphore tock_sem(0); 

void ticker(void) 
{ 
    while(true) 
    { 
     P(tick_sem); 
     do_tick(); 
     V(tock_sem); 
    } 
} 

void tocker(void) 
{ 
    while(true) 
    { 
     P(tock_sem); 
     do_tock(); 
     V(tick_sem); 
    } 
} 

Sin embargo, si hago lo mismo con un mutex (que es técnicamente un semáforo binario), que tiene un olor extraño código.

std::mutex tick_mutex; 
std::mutex tock_mutex; 
tock_mutex.lock(); 

void ticker(void) 
{ 
    while(true) 
    { 
     tick_mutex.lock(); 
     do_tick(); 
     tock_mutex.unlock(); 
    } 
} 

void tocker(void) 
{ 
    while(true) 
    { 
     tock_mutex.lock() 
     do_tock(); 
     tick_mutex.unlock(); 
    } 
} 

Creo que el olor es que un mutex no está destinado a transmitir información a otro hilo. (El comité estándar de C++ 11 agregó un falso error al try_lock para vencer la transferencia de información inesperada; §30.4.1/14.) Parece que los mutexes están destinados a sincronizar el acceso a una variable, que luego puede transmitir información a otro hilo.

Por último, cuando se implementa con un std::condition_variable, parece correcto, pero es más complicado (una variable tick_vs_tock, un mutex y una variable de condición). He omitido la implementación por brevedad, pero es realmente sencillo.

¿La solución mutex está bien? ¿O hay algo sutilmente mal con eso?

¿Hay un buen patrón para resolver mi problema de tic/tac que no he pensado?

+0

BTW: este problema solo surgió porque C++ 0x no tiene un 'std :: semáforo' y la doble solución' std :: mutex' es menos complicada que la solución 'std :: condition_variable'. –

+3

(¿Por qué) es incluso válido para 'desbloquear()' un mutex en un hilo que no lo contiene actualmente? –

+0

@Steve esa es realmente una buena pregunta (sugerencia, sugerencia, empujoncito, empujón). –

Respuesta

10

Un Mutex no es simplemente un semáforo binario, también tiene la limitación de que solo el hilo de bloqueo puede desbloquearlo.

Usted está infringiendo esa regla.

Editar:

De MSDN:

La función ReleaseMutex falla si el subproceso de llamada no es dueño del objeto mutex .

De algún sitio que Google se presentaron a pthread_mutex_unlock: función

El pthread_mutex_unlock() puede fallar si:

EPERM El hilo actual no es propietaria de la exclusión mutua.

Y encontrará lo mismo en otras implementaciones de mutex. Tiene sentido porque se supone que un mutex protege el acceso de un hilo a un recurso, por lo que otro hilo no debería poder desbloquearlo.

+0

Según el comentario de Steve. ¿Es eso realmente una regla? Definitivamente es una buena idea. –

+0

@deft: creo que depende de la implementación del mutex, que depende del sistema. En general, no es una buena idea desbloquear el mutex de otra persona, incluso si el sistema lo permite. – littleadv

+0

@deft_code: Todavía no he leído la sección de exclusión mutua del FDIS, pero me sorprendería si * no es * una regla. Los mutex tienen un propietario, es parte de su definición fundamental como herramienta de sincronización. –

9

Como tiene un caso para utilizar un semáforo, creo que la solución es portably implement one using a mutex and a condition variable.

Esto puede no ser especialmente eficiente (ya que usará un par mutex/condvar por semáforo), pero puede cambiar en una implementación alternativa en sistemas que tienen sus propios semáforos (como Posix y Windows).

Aparentemente semaphores are "too error-prone". Con todo respeto a Boost, creo que al menos algunos de nosotros podemos manejarlo. Ciertamente puede unirse a nudos trying to do complicated things with multiple semaphores, y son una herramienta de bajo nivel. Pero cuando son lo correcto, no hay problema.

+0

Estoy de acuerdo con que la cosa "demasiado propensa a errores" es extraña. Los mutexes también pueden ser propensos a errores. Entonces puede un martillo. – asveikau