C ++ 14 parece haber omitido un mecanismo para verificar si un std::mutex
está bloqueado o no. Vea esta pregunta SO:
https://stackoverflow.com/questions/21892934/how-to-assert-if-a-stdmutex-is-locked
Hay varias formas de evitar esto, por ejemplo, usando;
std::mutex::try_lock()
std::unique_lock::owns_lock()
Pero ninguno de estos son soluciones particularmente satisfactorias.
try_lock()
se le permite devolver un falso negativo y tiene un comportamiento indefinido si el hilo actual ha bloqueado el mutex. También tiene efectos secundarios. owns_lock()
requiere la construcción de una unique_lock
encima del original std::mutex
.
Obviamente podría rodar el mío, pero preferiría entender las motivaciones para la interfaz actual.
La capacidad de verificar el estado de un mutex (p std::mutex::is_locked()
. Ej. ) No me parece una solicitud esotérica, por lo que sospecho que el Comité Estándar omitió deliberadamente esta característica en lugar de ser un descuido.
¿Por qué?
Editar: Ok, entonces tal vez este caso de uso no sea tan común como esperaba, así que ilustraré mi escenario particular. Tengo un algoritmo de aprendizaje automático que se distribuye en múltiples hilos. Cada subproceso funciona de forma asíncrona y vuelve a un grupo maestro una vez que ha completado un problema de optimización.
Luego bloquea un mutex maestro. Luego, el subproceso debe elegir un nuevo padre desde el cual mutar una descendencia, pero solo puede elegir entre los padres que actualmente no tienen descendencia que están siendo optimizados por otros subprocesos. Por lo tanto, necesito realizar una búsqueda para encontrar padres que actualmente no están bloqueados por otro hilo. No hay riesgo de que el estado del mutex cambie durante la búsqueda, ya que el mutex del hilo maestro está bloqueado. Obviamente hay otras soluciones (actualmente estoy usando un indicador booleano) pero pensé que el mutex ofrece una solución lógica a este problema, ya que existe con el propósito de sincronización entre subprocesos.
is_locked
?Respuestas:
Puedo ver al menos dos problemas graves con la operación sugerida.
El primero ya fue mencionado en un comentario de @ gnasher729 :
La única manera de asegurarse de que la propiedad "está bloqueada actualmente" de un mutex no cambia es, bueno, bloquearla usted mismo.
El segundo problema que veo es que, a menos que bloquee un mutex, su hilo no se sincroniza con el hilo que previamente había bloqueado el mutex. Por lo tanto, ni siquiera está bien definido hablar sobre "antes" y "después" y si el mutex está bloqueado o no es como preguntar si el gato de Schrödiger está vivo sin intentar abrir la caja.
Si entiendo correctamente, ambos problemas serían discutibles en su caso particular gracias a que el mutex maestro está bloqueado. Pero este no parece ser un caso particularmente común para mí, así que creo que el comité hizo lo correcto al no agregar una función que podría ser algo útil en escenarios muy especiales y causar daños en todos los demás. (En el espíritu de: "Hacer que las interfaces sean fáciles de usar correctamente y difíciles de usar incorrectamente")
Y si puedo decir, creo que la configuración que tiene actualmente no es la más elegante y podría ser refactorizada para evitar el problema por completo. Por ejemplo, en lugar de que el hilo maestro verifique a todos los padres potenciales por uno que no esté bloqueado actualmente, ¿por qué no mantener una cola de padres listos? Si un hilo quiere optimizar otro, saca el siguiente de la cola y, tan pronto como tiene nuevos padres, los agrega a la cola. De esa manera, ni siquiera necesita el hilo maestro como coordinador.
fuente
Parece que está utilizando los mutexes secundarios para no bloquear el acceso a un problema de optimización, sino para determinar si un problema de optimización se está optimizando en este momento o no.
Eso es completamente innecesario. Tendría una lista de problemas que necesitan optimización, una lista de problemas que se están optimizando en este momento y una lista de problemas que se han optimizado. (No tome "lista" literalmente, suponga que significa "cualquier estructura de datos apropiada").
Las operaciones de agregar un nuevo problema a la lista de problemas no optimizados, o mover un problema de una lista a la siguiente, se realizarían bajo la protección del único mutex "maestro".
fuente
std::mutex
es apropiado para dicha estructura de datos?std::mutex
se basa en una implementación de mutex definida por el sistema operativo que bien puede tomar recursos (por ejemplo, identificadores) que son limitados y lentos para asignar y / o operar. Es probable que el uso de un único mutex para bloquear el acceso a una estructura de datos interna sea mucho más eficiente y posiblemente también más escalable.Como otros dijeron, no hay ningún caso de uso en
is_locked
el que un mutex sea beneficioso, por eso la función no existe.El caso con el que tiene un problema es increíblemente común, es básicamente lo que hacen los subprocesos de trabajo, que son una de las implementaciones de subprocesos , si no la más común.
Tienes un estante con 10 cajas. Tienes 4 trabajadores trabajando con estas cajas. ¿Cómo se asegura de que los 4 trabajadores trabajen en diferentes cajas? El primer trabajador saca una caja del estante antes de comenzar a trabajar en ella. El segundo trabajador ve 9 cajas en el estante.
No hay mutexes para bloquear las cajas, por lo que no es necesario ver el estado del mutex imaginario en la caja, y abusar de un mutex como booleano es simplemente incorrecto. El mutex bloquea el estante.
fuente
Además de las dos razones dadas en la respuesta anterior de 5gon12eder, me gustaría agregar que no es necesario ni deseable.
Si ya tiene un mutex, ¡es mejor que sepa que lo tiene! No necesitas preguntar. Al igual que con poseer un bloque de memoria o cualquier otro recurso, debe saber exactamente si lo posee o no, y cuándo es apropiado liberar / eliminar el recurso.
Si ese no es el caso, su programa está mal diseñado y se dirige a problemas.
Si necesita acceder al recurso compartido protegido por el mutex, y aún no tiene el mutex, entonces necesita adquirir el mutex. No hay otra opción, de lo contrario, la lógica de su programa no es correcta.
Es posible que el bloqueo sea aceptable o inaceptable, en cualquier caso,
lock()
otry_lock()
le dará el comportamiento que desea. Todo lo que necesita saber, positivamente y sin duda, es si adquirió con éxito el mutex (el valor de retorno detry_lock
le dice). Es intrascendente si alguien más lo tiene o si tiene una falla espuria.En cualquier otro caso, sin rodeos, no es asunto tuyo. No necesita saber, y no debe saber, ni hacer suposiciones (por los problemas de sincronización y puntualidad mencionados en la otra pregunta).
fuente
try_lock
el primer recurso y, si ese falla, prueba el segundo. Ejemplo: Tres conexiones agrupadas persistentes al servidor de la base de datos, y debe usar una para enviar un comando.is_locked()
esa que podrían facilitar ese comportamiento.is_locked
, existe una solución mucho mejor para su problema que la que tiene en mente.Es posible que desee utilizar atomic_flag con el orden de memoria predeterminado. No tiene carreras de datos y nunca arroja excepciones como mutex con múltiples llamadas de desbloqueo (y aborta sin control, podría agregar ...). Alternativamente, hay atómico (por ejemplo, atómico [bool] o atómico [int] (con corchetes triangulares, no [])), que tiene buenas funciones como load y compare_exchange_strong.
fuente
Quiero agregar un caso de uso para esto: habilitaría una función interna para garantizar como condición previa / afirmación que la persona que llama realmente mantiene el bloqueo.
Para las clases con varias de estas funciones internas, y posiblemente muchas funciones públicas que las llamen, podría garantizar que alguien que agregue otra función pública que llame a la interna efectivamente adquiera el bloqueo.
fuente