2010-11-17 11 views
9

Sin mantener una lista de hilos actuales, intento ver que se entregue una señal en tiempo real a todos los hilos de mi proceso. Mi idea es hacerlo así:Señalización de todos los hilos en un proceso

  • Inicialmente, el controlador de señal está instalado y la señal está desbloqueada en todos los hilos.
  • Cuando un hilo desea enviar la señal de "emisión", adquiere un mutex y establece un indicador global de que la transmisión se está llevando a cabo.
  • El emisor bloquea la señal (usando pthread_sigmask) por sí mismo, e ingresa repetidamente en un bucle llamando a raise(sig) hasta que sigpending indica que la señal está pendiente (no había hilos restantes con la señal bloqueada).
  • Cuando los hilos reciben la señal, actúan sobre ella pero esperan en el manejador de señal para que se borre la señal de transmisión, de modo que la señal permanecerá enmascarada.
  • El emisor termina el ciclo desbloqueando la señal (para obtener su propia entrega).
  • Cuando el remitente maneja su propia señal, borra la bandera global para que todos los demás hilos puedan continuar con su actividad.

El problema que me estoy encontrando es que pthread_sigmask no se está respetando. Todo funciona bien si ejecuto el programa de prueba bajo strace (presumiblemente debido a diferentes tiempos de programación), pero tan pronto como lo ejecuto solo, el emisor recibe su propia señal (a pesar de haberlo bloqueado ...) y ninguno de los otros hilos alguna vez programado.

¿Alguna idea de lo que podría estar mal? He intentado usar sigqueue en lugar de raise, probando la máscara de señal, agregando sleep por todos lados para asegurarme de que los hilos esperan pacientemente sus señales, etc. y ahora estoy perdido.

Edit: Gracias a la respuesta de psmears, creo que entiendo el problema. Aquí hay una solución potencial. La retroalimentación sería genial:

  • En cualquier momento, puedo saber el número de subprocesos en ejecución, y puedo evitar toda creación de subprocesos y salir durante la señal de transmisión si es necesario.
  • El hilo que desea hacer la señal de emisión adquiere un bloqueo (para que ningún otro hilo pueda hacerlo al mismo tiempo), luego bloquea la señal por sí mismo, y envía señales num_threads al proceso, luego desbloquea la señal por sí mismo .
  • El manejador de señal incrementa atómicamente un contador, y cada instancia del manejador de señal espera hasta que ese contador sea igual a num_threads para regresar.
  • El hilo que hizo la transmisión también espera que el contador llegue al num_threads, luego libera el bloqueo.

Una posible preocupación es que las señales no se pondrán en cola si el kernel está sin memoria (Linux parece tener ese problema). ¿Sabe si sigqueue informa confiablemente a la persona que llama cuando no puede poner la señal en cola (en cuyo caso yo haría un bucle hasta que tenga éxito), o las señales podrían perderse silenciosamente?

Editar 2: Parece que está trabajando ahora. De acuerdo con la documentación para sigqueue, devuelve EAGAIN si no pone en cola la señal.Pero para mayor robustez, decidí seguir llamando al sigqueue hasta que se ejecuten los manejadores de señal num_threads-1, intercalando llamadas a sched_yield después de que he enviado las señales num_threads-1.

Hubo una condición de carrera en el tiempo de creación de subprocesos, contando nuevos hilos, pero lo resolví con un extraño (ab) uso de bloqueos de lectura-escritura. La creación de subprocesos es "lectura" y la señal de difusión es "escritura", por lo tanto, a menos que haya un hilo tratando de transmitir, no crea ninguna contención en la creación de subprocesos.

+0

OK, si * todas * las actualizaciones de los datos tocados por el manejador de señal son atómicas, entonces esto se está acercando al funcionamiento :) Creo que todavía hay una condición de carrera si todos los subprocesos esperan que el contador sea 'num_threads' : si ocurre otro evento de transmisión * inmediatamente * después del primero, entonces es posible que el contador regrese a 0 antes de que todos los hilos hayan notado que es igual a 'núm_hilos'. Creo que esto se puede resolver con una variable de condición. También desaconsejaría la espera de ocupado (incluso con 'sched_yield'), esto puede provocar una gran carga de la CPU (¡y una duración de la batería baja si es relevante!) – psmears

+0

Otro posible problema que puede probar es que es posible una señal para ser entregada a un hilo después de haber sido creada en el kernel, pero antes de que sus datos locales del hilo estén completamente configurados (es decir, en el tiempo entre el inicio del hilo y cuando llama a la función pasada a pthread_create))) ... – psmears

+0

Todas las actualizaciones son atómicas porque todas ocurren en el kernel-space a través de syscalls que afectan erróneamente solo el hilo que llama y no el proceso; este código es una solución. :-) Solo puede ocurrir un evento de transmisión a la vez; toda la función está protegida por un mutex global. En cuanto a la espera ocupada, tal vez funcione una espera temporizada de una variable condicional con un tiempo de espera muy corto, pero desde entonces me he encontrado teniendo problemas con la entrega de muy pocas señales a menos que siga llamando a 'sigqueue' hasta que funcione. –

Respuesta

4

raise() envía la señal al hilo actual (solamente), por lo que otros hilos no lo recibirán. Sospecho que el hecho de que strace hace que las cosas funcionen es un error en strace (debido a la forma en que funciona, termina interceptando todas las señales enviadas al proceso y volviendo a subirlas, por lo que puede estar volviendo a subirlas de forma incorrecta ...).

Probablemente pueda evitar el uso de kill(getpid(), <signal>) para enviar la señal al proceso actual como un todo.

Sin embargo, otro problema potencial que puede ver es que sigpending() puede indicar que la señal está pendiente en el proceso antes de que todos los hilos lo hayan recibido; todo eso significa que hay al menos una señal pendiente para el proceso, y aún no hay CPU disponible para ejecutar un hilo para entregarlo ...

¿Puede describir más detalles de lo que pretende lograr? ¿Y qué tan portátil quieres que sea? Es casi seguro que hay una forma mejor de hacerlo (las señales casi siempre son un gran dolor de cabeza, especialmente cuando se mezclan con hilos ...)

+0

Tenía la impresión de que la señal no aparecería como pendiente a menos que todos los hilos estuvieran bloqueándola. Pero sospecho que su respuesta aquí es correcta: la primera vez, el ciclo envía una señal, y como ningún otro hilo se programa inmediatamente para manejarlo, aparece como pendiente, y el hilo de envío lo desbloquea y lo maneja él mismo. .. :-( –

+0

En cuanto a lo que estoy tratando de lograr, quiero poder ejecutar un fragmento de código en cada hilo (haciendo uso del estado local del subproceso) sin tener que hacer un seguimiento de una lista de todos ejecutando La idea detrás de ese requerimiento es que este código que necesita ejecutarse en todos los hilos rara vez se usa (casi nunca), mientras que la creación y la salida de hilos pueden ser muy frecuentes, y no quiero imponer el costo de sincronizar contabilidad de hilos solo para una operación casi nunca utilizada. –

+0

¿Existe la posibilidad de que el hilo se actualice por el hilo en el momento en que se llama al controlador de señal? De ser así, el controlador de señal lo puede ver (y actualizar es?) mientras que es inconsistente carpa, que puede causar problemas (y no se puede llamar con seguridad a malloc(), free(), * printf() etc por la misma razón!) ... ¿qué están haciendo realmente tus hilos? ¿Esperan el bucle en espera de eventos (por ejemplo, llamar a select()), o hacer un procesamiento masivo, o una variedad de cosas diferentes? – psmears

1

En multiproceso, el aumento de programa (sig) es equivalente a pthread_kill (pthread_self(), sig). Pruebe kill (getpid(), sig)

0

Dado que aparentemente puede bloquear la creación y destrucción de hilos, ¿no podría simplemente tener la "transmisión"? "¿Enlazar las actualizaciones requeridas al estado local del subproceso en una cola por subproceso, que cada subproceso comprueba cada vez que va a usar el estado local de subproceso? Si hay actualizaciones pendientes, primero las aplica.

+0

No, por dos razones. Uno es una cuestión de costo. La idea aquí es minimizar el costo de algo que debe poder suceder, pero casi nunca sucede en la realidad (la mayoría de los programas ni siquiera usan la funcionalidad en absoluto). La otra razón es que no tengo una lista de hilos para llegar a su estado local. Seguramente podría agregar una lista, pero eso agregaría un costo adicional (bloqueo adicional en la creación y terminación del hilo). –

0

Está intentando sincronizar un conjunto de hilos. Desde un punto de vista de patrón de diseño, la solución nativa pthread para su problema sería una barrera pthread.

Cuestiones relacionadas