POSIX permite a los mutexes a ser recursivo. Eso significa que el mismo hilo puede bloquear el mismo mutex dos veces y no interbloqueo. Por supuesto, también es necesario para desbloquear dos veces, de lo contrario ningún otro hilo puede obtener el mutex. No todos los sistemas de apoyo de pthreads también apoyo recursiva mutexes, pero si quieren ser POSIX cumplir, tienen que.

Otras Api (más alto nivel Api) también suelen ofrecer los mutexes, a menudo llamados Bloqueos. Algunos sistemas/idiomas (por ejemplo, el Cacao Objective-C) ofrecer tanto, recursivos y no recursivos mutexes. Algunos idiomas también solo ofrecen uno o el otro. E. g. en Java los mutexes son siempre recursiva (el mismo hilo dos veces «sincronizar» en el mismo objeto). Dependiendo de lo otro hilo de la funcionalidad que ofrecen, no tener recursiva mutexes podría ser un problema, ya que fácilmente puede ser escrito (ya he implementado recursiva mutexes a mí mismo sobre la base de la más simple mutex/condición de las operaciones).

Lo que yo no entiendo muy bien: ¿cuáles son los no-recursiva mutexes buenas? ¿Para qué quiero tener un hilo de interbloqueo si se bloquea el mismo mutex dos veces? Incluso lenguajes de alto nivel que podría evitar que (por ejemplo, pruebas, si este se interbloqueo y lanzar una excepción si lo hace) por lo general no hacen eso. Van a dejar que el hilo de interbloqueo en su lugar.

Es solamente para los casos donde accidentalmente dos veces y sólo lo desbloquee de una vez y en caso de un recurrente mutex, sería más difícil encontrar el problema, en lugar de eso tengo que interbloqueo de inmediato para ver dónde está el bloqueo incorrecto aparece? Pero no puedo hacer lo mismo con el hecho de tener un bloqueo de contador devuelve cuando desbloqueo y en una situación, donde estoy seguro de que se liberó la última de bloqueo y el contador no es cero, se puede lanzar una excepción de registro o el problema? O hay alguna otra, más útil el uso-caso de no recursiva de los mutexes de que yo no vea? O es que tal vez sólo el rendimiento, como un no-recursiva mutex puede ser ligeramente más rápido que un recursiva uno? Sin embargo, he probado esta y la diferencia no es tan grande.

InformationsquelleAutor Mecki | 2008-10-09

6 Comentarios

  1. 141

    La diferencia entre un recursivos y no recursivos mutex tiene que ver con la propiedad. En el caso de un recurrente mutex, el kernel tiene que mantener un seguimiento de los hilos que obtiene realmente el mutex la primera vez de modo que pueda detectar la diferencia entre la recursividad frente a un subproceso diferente que debe bloquear en su lugar. Como otra respuesta se señaló, no es una cuestión de la sobrecarga adicional de este en términos de memoria para almacenar este contexto y también los ciclos necesarios para el mantenimiento de la misma.

    Sin embargo, hay otras consideraciones en juego aquí también.

    Porque el recurrente mutex tiene un sentido de la propiedad, el hilo que se agarra a la exclusión mutua debe ser el mismo hilo que libera el mutex. En el caso de los no-recursiva mutexes, no hay sentido de propiedad y de cualquier hilo puede suelen liberar el mutex no importa el hilo que originalmente tuvo la exclusión mutua. En muchos casos, este tipo de «mutex» es en realidad más de un semáforo de acción, donde no se utiliza necesariamente la exclusión mutua como un dispositivo de exclusión sino como la sincronización o dispositivo de señalización entre dos o más hilos.

    Otra propiedad que viene con un sentido de propiedad en un mutex es la capacidad de la prioridad de apoyo a la herencia. Debido a que el núcleo puede seguir el hilo de la posesión de la exclusión mutua y también la identidad de todos los bloqueador(s), en una prioridad de la rosca del sistema se hace posible aumentar la prioridad del hilo que actualmente posee la exclusión mutua a la prioridad del subproceso de prioridad más alto que es en la actualidad el bloqueo en el mutex. Esta herencia se evita el problema de la inversión de la prioridad que puede ocurrir en tales casos. (Tenga en cuenta que no todos los sistemas de prioridad de apoyo a la herencia de tales exclusiones mutuas, pero es otra característica que se hace posible a través de la noción de propiedad).

    Si te refieres a la clásica VxWorks RTOS kernel, se definen tres mecanismos:

    • mutex – soporta la recursividad, y, opcionalmente, la prioridad de la herencia
    • semaphore binario – no la recursividad, la herencia, la simple exclusión, tomador y el dador no tiene que ser el mismo hilo, autorización de transmisión disponible
    • contar semáforo – no la recursividad o la herencia, actúa como un coherente de los recursos contador desde cualquier conteo inicial, hilos solo bloque donde neta de contar en su contra el recurso es cero.

    De nuevo, esto depende un poco de la plataforma, especialmente lo que ellos llaman estas cosas, pero esta debe ser representativa de los conceptos y de distintos mecanismos en juego.

    • su explicación acerca de la no-recursiva mutex sonaba más como un semáforo. Un mutex (si recursiva o no recursivo ) tiene una noción de la propiedad.
    • Es muy confuso cuando la gente discute sobre cosas como estas.. así que es la entidad que define estas cosas?
    • La norma pertinente. Esta respuesta, por ejemplo, es malo para posix (threads) , donde el desbloqueo normal exclusión mutua en un hilo distinto al hilo que bloqueado es un comportamiento indefinido, mientras que hacer lo mismo con un error de comprobación o recursiva mutex resultados en un previsible código de error. Otros sistemas y los criterios pueden comportarse de manera muy diferente.
    • Que es el temor-en Realidad-algunos..Gracias
    • Tal vez este es ingenuo, pero yo estaba bajo la impresión de que la idea central de un mutex es que la rosca de bloqueo libera el mutex y, a continuación, otros subprocesos pueden hacer lo mismo. Desde computing.llnl.gov/tutorials/pthreads:
    • Presiona entrar demasiado pronto: Desde computing.llnl.gov/tutorials/pthreads: «pthread_mutex_unlock() se desbloquea un mutex llamado por el subproceso propietario. Llamar a esta rutina es necesaria después de una rosca ha completado su uso de datos protegidos si otros hilos se van a adquirir el mutex para su trabajo con los datos protegidos. Se devolverá un error si: Si el mutex ya estaba desbloqueado Si el mutex es propiedad de otro hilo»
    • ¿Significa esta respuesta es incorrecta?
    • ¿Qué es la «difusión de la liberación»?

  2. 116

    La respuesta es no eficiencia. No reentrante mutexes conducir a un mejor código.

    Ejemplo: a::foo() adquiere el bloqueo. A continuación, llama a B::bar(). Esto funcionó bien cuando la escribió. Pero en algún momento más tarde, alguien cambios B::bar() para llamar a Un::baz(), que también adquiere el bloqueo.

    Bien, si usted no tiene recursiva mutexes, este interbloqueos. Si los tiene, se ejecuta, pero se puede romper. Un::foo() puede haber dejado el objeto en un estado incoherente antes de llamar a bar(), en el supuesto de que baz() no pudo conseguir que se ejecute, ya que también adquiere la exclusión mutua. Pero probablemente no se debería correr! La persona que escribió Un::foo() asume que nadie podría llamar a Un::baz() al mismo tiempo – que es toda la razón de que ambos de estos métodos adquiridos en la cerradura.

    El derecho de los modelos mentales para el uso de mutexes: El mutex protege un invariante. Cuando el mutex se lleva a cabo, el invariante puede cambiar, pero antes de soltar la exclusión mutua, el invariante es re-establecido. Reentrada bloqueos son peligrosos porque la segunda vez que adquirir el bloqueo no se puede estar seguro de que el invariante es cierto más.

    Si usted es feliz con reentrada bloqueos, es sólo porque no han tenido que depurar un problema como este antes. Java tiene no reentrante bloquea estos días en java.util.concurrente.cerraduras, por el camino.

    • Me tomó un tiempo para conseguir lo que se dice sobre el invariante no ser válida cuando tome el bloqueo de un segundo de tiempo. Buen punto! Lo que si se tratase de una lectura de bloqueo de escritura (como Java ReadWriteLock) y adquirió el bloqueo de lectura y, a continuación, re-adquirido el bloqueo de lectura por segunda vez en el mismo hilo. No invalidar un invariante después de adquirir un bloqueo de lectura de derecha? Así que cuando usted adquiera el segundo bloqueo de lectura, el invariante es cierto.
    • No Java no reentrante bloquea estos días en java.util.concurrente.cerraduras??
    • +1 creo que el uso más común para la reentrada de bloqueo está dentro de una sola clase, donde algunos de los métodos que pueden ser llamados desde ambos vigilados y no vigilados de fragmentos de código. Esto puede ser en realidad siempre deja fuera. @user454322 Seguro, Semaphore.
    • Perdón mi falta de comprensión, pero yo no veo cómo esto es relevante para la exclusión mutua. Supongamos que no hay multithreading y de bloqueo de los involucrados, A::foo() todavía puede tener a la izquierda el objeto en un estado incoherente antes de llamar a A::bar(). ¿Qué mutex, recursiva o no, no tiene nada que ver con este caso?
    • La cuestión es ser capaz de razonar localmente sobre el código. La gente (al menos para mí) son entrenados para reconocer las regiones bloqueadas como invariante mantenimiento, que es en el momento de adquirir el bloqueo ningún otro subproceso está modificando el estado, por lo que el invariantes en la región crítica de espera. Esto no es una regla fija, y puede codificar con los invariantes no se mantiene en la mente, pero que acaba de hacer el código más difícil de la razón y de mantener. Lo mismo sucede en el subproceso único modo sin exclusiones mutuas, pero no estamos entrenados a la razón localmente alrededor del área protegida.
    • Esta es la mejor respuesta, pero no universal. Estamos capacitados para métodos reconocidos de contenido como invariante mantenimiento. Así que cuando se llama a bar de foo uno debe asegurarse de los invariantes son suficientes para bar, pero eso no quiere decir que la operación más amplia foo está haciendo, se puede cortar y dejar paso a otros hilos. Recursiva cerraduras tiene sentido, de lo contrario que significaría que podría liberar el bloqueo antes de llamar a bar. La mayoría del tiempo usted realmente no puede, aunque los invariantes han sido atendidos. Porque en la continuación (el resto de foo) que hacen que las expectativas de vida desde su inicio.
    • foo() { f1(); bar(); f2(); } f2 es la continuación. este es secuencial de acoplamiento se podría decir, pero f2 invariantes son mayores que los de la barra de invariantes, que dependen de la labor realizada por la f1 y bar junto, y sin cortar. foo es atómico pack o un peligro. recusrive bloqueos de resolver esto.
    • Hay un error en esta respuesta: «La persona que escribió Un::foo() asume que nadie podría llamar a Un::baz() al mismo tiempo». Si es el mismo hilo, que no al mismo tiempo. Recursiva mutexes todavía son correctos en todos los sentidos, excepto la eficiencia.
    • La punta de la cuestión no tiene nada que ver con los mutexes y multithreading. Es un problema común. Usted puede felizmente lograr esta situación con foo() de la barra() y baz() en cualquier subproceso único programa.
    • Es en el mismo hilo y en el mismo tiempo.

  3. 85

    Como escrito por Dave Butenhof mismo:

    «El más grande de todos los grandes problemas con recursiva mutexes es que
    les animamos a perder la pista de su esquema de bloqueo y
    ámbito de aplicación. Este es mortal. Mal. Es el «hilo eater». Se mantienen bloqueos de
    el absolutamente menor tiempo posible. Período. Siempre. Si usted está llamando
    algo con un bloqueo, simplemente porque usted no sabe que se lleva a cabo, o
    porque no sabes si el destinatario de las necesidades de la exclusión mutua, entonces estás
    mantiene demasiado tiempo. Usted está apuntando con una escopeta en su aplicación y
    apretar el gatillo. Que, presumiblemente, se inició el uso de hilos para conseguir
    la concurrencia; pero sólo has IMPEDIDO de simultaneidad.»

    • También tenga en cuenta la parte final en Butenhof respuesta: ...you're not DONE until they're [recursive mutex] all gone.. Or sit back and let someone else do the design.
    • Él también dice que el uso de una sola global recursiva mutex (su opinión es que sólo necesita uno) está bien como una muleta conscientemente posponer el trabajo duro de la comprensión de la invariances de una biblioteca externa cuando usted comienza a usarlo en el código multiproceso. Pero usted no debe usar muletas para siempre, pero, finalmente, invertir el tiempo para entender y corregir la concurrencia de los invariantes del código. Así podemos parafrasear el que el uso recursivo de exclusión mutua es la deuda técnica.
  4. 13

    El derecho de los modelos mentales para el uso de
    exclusiones mutuas: El mutex protege un
    invariante.

    ¿Por qué estás seguro de que este es realmente el derecho de los modelos mentales para el uso de mutexes?
    Creo que un modelo es la protección de datos, pero no se invariantes.

    El problema de la protección de los invariantes presenta incluso en aplicaciones multihilo y no tiene nada en común con multi-threading y los mutexes.

    Además, si usted necesita para proteger invariantes, usted todavía puede utilizar el binario semáforo que nunca es recursiva.

    • Verdadero. Hay mejores mecanismos para proteger un invariante.
    • Este debe ser un comentario a la respuesta que ofrece esa declaración. Los Mutexes no sólo a proteger los datos, también protegen a los invariantes. Intente escribir algo simple contenedor (el más simple es una pila) en términos de atomics (donde los datos se protege a sí mismo) en lugar de los mutexes y usted va a entender la declaración.
    • Los Mutexes no proteger los datos, que protegen un invariante. Que invariante, aunque puede ser utilizado para proteger los datos.
  5. 4

    Una de las principales razones que recursiva mutexes es útil en el caso de acceder a los métodos múltiples veces por el mismo hilo. Por ejemplo, decir si el mutex lock es la protección de un banco A/c a retirar, entonces, si hay una cuota asociada con el retiro, a continuación, el mismo mutex tiene que ser utilizado.

  6. 3

    El único caso de uso de la recursividad mutua es cuando un objeto contiene varios métodos. Cuando cualquiera de los métodos de modificar el contenido del objeto, y por lo tanto debe bloquear el objeto antes de que el estado es coherente.

    Si el uso de los métodos otros métodos (es decir: addNewArray() llama a addNewPoint(), y finaliza con recheckBounds()), pero ninguna de esas funciones por sí mismos la necesidad de bloquear el mutex, entonces recursiva mutex es un ganar-ganar.

    Para cualquier otro caso (resolución de sólo una mala codificación, utilizando incluso en objetos diferentes) está claramente mal!

Dejar respuesta

Please enter your comment!
Please enter your name here