2009-11-15 10 views
8

¿Cómo puedo esperar a que termine un subproceso desconectado en C++?Espere a que termine un subproceso desconectado en C++

No me importa un estado de salida, solo quiero saber si el hilo ha terminado o no.

Estoy tratando de proporcionar un contenedor síncrono alrededor de una herramienta thirdarty asíncrona. El problema es un accidente de condición de carrera extraño que implica una devolución de llamada. La progresión es:

  1. que llamo el thirdparty, y registrar una devolución de llamada
  2. cuando termina de terceros, que me notifica mediante la devolución de llamada - en un hilo separado tengo ningún control real sobre.
  3. Quiero que el hilo de (1) espere hasta que se llame (2).

Quiero envolver esto en un mecanismo que proporciona una llamada de bloqueo. Hasta el momento, no tengo:

class Wait { 
    public: 
    void callback() { 
    pthread_mutex_lock(&m_mutex); 
    m_done = true; 
    pthread_cond_broadcast(&m_cond); 
    pthread_mutex_unlock(&m_mutex); 
    } 

    void wait() { 
    pthread_mutex_lock(&m_mutex); 
    while (!m_done) { 
     pthread_cond_wait(&m_cond, &m_mutex); 
    } 
    pthread_mutex_unlock(&m_mutex); 
    } 

    private: 
    pthread_mutex_t m_mutex; 
    pthread_cond_t m_cond; 
    bool   m_done; 
}; 

// elsewhere... 
Wait waiter; 
thirdparty_utility(&waiter); 
waiter.wait(); 

Por lo que yo puedo decir, esto debería funcionar, y lo hace normalmente, pero a veces se bloquea. Por lo que yo puedo determinar a partir del fichero de núcleo, yo creo que el problema es el siguiente:

  1. Cuando la devolución de llamada emite el final de m_done, el hilo espera despierta
  2. El hilo espera ahora se hace aquí, y Esperar es destruido. Todos los miembros de Wait se destruyen, incluidos el mutex y el cond.
  3. El hilo de devolución de llamada intenta continuar desde el punto de difusión, pero ahora está utilizando la memoria que se ha liberado, lo que da como resultado daños en la memoria.
  4. Cuando el hilo de devolución de llamada intenta regresar (por encima del nivel de mi pobre método de devolución de llamada), el programa falla (generalmente con un SIGSEGV, pero he visto SIGILL un par de veces).

He intentado muchos mecanismos diferentes para tratar de solucionar esto, pero ninguno de ellos resuelve el problema. Todavía veo choques ocasionales.

EDITAR: Más detalles:

Esto es parte de una aplicación masivamente multiproceso, por lo que la creación de una espera estática no es práctico.

Ejecuté una prueba, creando Esperar en el montón, y filtrando deliberadamente la memoria (es decir, los objetos de espera nunca se desasignan), y eso no ocasionó bloqueos. Así que estoy seguro de que se trata de un problema de espera que se desasignará demasiado pronto.

También he intentado una prueba con un sleep(5) después del desbloqueo en wait, y que también no produjo fallas. Sin embargo, odio confiar en un kludge así.

EDITAR: detalles de terceros:

Yo no creo que esto era relevante al principio, pero cuanto más lo pienso, más creo que es el verdadero problema:

El material thirdparty Mencioné, y por qué no tengo control sobre el hilo: esto es usando CORBA.

Por lo tanto, es posible que CORBA retenga una referencia a mi objeto por más tiempo de lo previsto.

Respuesta

3

Sí, creo que lo que estás describiendo está sucediendo (condición de carrera en desasignar). Una manera rápida de arreglar esto es crear una instancia estática de Espera, una que no se destruirá. Esto funcionará siempre que no necesite tener más de un camarero al mismo tiempo.

También usará permanentemente esa memoria, no se destrabará. Pero no parece que eso sea tan malo.

El problema principal es que es difícil coordinar la vida útil de las construcciones de comunicación entre subprocesos: siempre necesitará al menos una construcción de comunicación sobrante para comunicarse cuando sea seguro destruirla (al menos en idiomas sin recolección de basura, como C++).

EDIT: Consulte los comentarios de algunas ideas sobre refcounting con un mutex global.

+0

Desafortunadamente, esto se encuentra en una aplicación de subprocesos múltiples, y realmente queremos objetos separados para cada uno, de lo contrario, nos ralentiza demasiado. – Tim

+0

Además, si usamos un Wait estático, existe el problema de tratar de coordinar qué hilo debe reanudarse. – Tim

+0

Ok, puedes hacer esto.Puede agregar un campo de refcount al objeto Wait, protegido por un mutex global. Comience el refcount en 2, y luego haga que la devolución de llamada y el mesero disminuyan el recuento cuando termine. Si el mutex global se convierte en su cuello de botella, existen otras soluciones más complicadas. –

0

Por lo que yo sé, no hay una forma portátil de preguntar directamente un hilo si se está ejecutando (es decir, no la función pthread_). Lo que está haciendo es la manera correcta de hacerlo, al menos en cuanto a tener una condición que usted señalice. Si ve bloqueos que está seguro se deben a que el objeto Wait se está desasignando cuando se cierra el hilo que lo crea (y no algún otro otro bloqueo sutil, todo es muy común), el problema es que debe hacer Asegúrese de que Waitno está siendo desasignado, al administrar desde un hilo que no sea el que hace la notificación. Ponlo en la memoria global o dinámicamente asignarlo y compartirlo con ese hilo. La mayoría simplemente no tienen el hilo esperando en la memoria para el Wait, tienen el hilo haciendo la espera.

0

¿Está inicializando y destruyendo el mutex y condiciona var correctamente?

Wait::Wait() 
{ 
    pthread_mutex_init(&m_mutex, NULL); 
    pthread_cond_init(&m_cond, NULL); 
    m_done = false; 
} 

Wait::~Wait() 
{ 
    assert(m_done); 
    pthread_mutex_destroy(&m_mutex); 
    pthread_cond_destroy(&m_cond); 
} 

Asegúrese de que no se está destruyendo de forma prematura el objeto Wait - si se destruye en un hilo, mientras que el otro hilo todavía lo necesita, usted obtendrá una condición de carrera que probablemente resultará en un error de segmentación . Recomiendo que sea una variable estática global que se construya en la inicialización del programa (antes de main()) y se destruya al salir del programa.

+0

sí, el mutex y el cond se inicializan/destruyen correctamente. En realidad estoy usando clases de envoltura en aquellas que han sido bien probadas. Y sí, estoy seguro de que Wait se está destruyendo prematuramente, mientras que un hilo todavía está en Wait :: callback. – Tim

0

Si su suposición es correcta, entonces el módulo de un tercero parece tener errores y necesita encontrar algún tipo de truco para que su aplicación funcione.

Estático Wait no es factible. ¿Qué hay de Wait grupo (incluso puede crecer a pedido)? ¿Está utilizando la aplicación utilizando el grupo de subprocesos? Aunque todavía existe la posibilidad de que se vuelva a utilizar el mismo Wait mientras el módulo de un tercero todavía lo esté utilizando. Pero puede minimizar esa posibilidad al consultar correctamente Waits vacantes en su grupo.

Descargo de responsabilidad: de ninguna manera soy un experto en seguridad de hilos, así que considere esta publicación como una sugerencia de un lego.

Cuestiones relacionadas