Para los subprocesos de POSIX de C Linux, ¿qué es un subproceso que es un estado que se puede unir; ¿En qué se diferencia de un estado separado y cómo debo elegir para que mis hilos se puedan unir o separar?

A2A

Esta es una pregunta un tanto compleja, porque está mezclando detalles de implementación en diferentes capas y porque es difícil usar una analogía de proceso, a menos que tenga una comprensión relativamente sustancial de la relación entre los procesos padre e hijo.

Comencemos con solo hilos, y el nivel en el que está llegando a cierta confusión.

Cuando se crea un hilo, se crea con ciertos atributos de hilo. Estos pueden especificarse antes de llamar a pthread_create , o pueden estar predeterminados.

Uno de estos es si puede o no llamar a pthread_join en el hilo en algún momento posterior en el tiempo. Este atributo es detachstate .

El atributo detachstate puede tomar uno de dos valores:

  • PTHREAD_CREATE_JOINABLE – este es el valor predeterminado
  • PTHREAD_CREATE_DETACHED : esto se puede configurar

¿Cómo configura PTHREAD_CREATE_DETACHED para que no se pueda PTHREAD_CREATE_DETACHED un hilo? Hay dos maneras:

  • La primera es configurar un valor pthread_attr_t y luego llamar a pthread_attr_setdetachstate con un parámetro detachstate de PTHREAD_CREATE_DETACHED . Cuando posteriormente llamas pthread_create , pasas este pthread_attr_t a pthread_create .

    Esto establece que cuando se llama a pthread_exit en ese hilo, simplemente desaparecerá, en lugar de permanecer en un estado que se puede unir hasta que pthread_join ; es un error llamar a pthread_detach desde dicho hilo.

  • El segundo es creando el hilo que se puede unir; esto se logra al pasar no pthread_attr_t a la llamada pthread_create , o al establecer un atributo, pero dejando el valor de detachstate como predeterminado, o llamando explícitamente a pthread_attr_setdetachstate en el atributo con un parámetro de detachstate de PTHREAD_CREATE_JOINABLE . A menos que haya modificado previamente el atributo, este será el predeterminado de todos modos.

    Más tarde, usted llama a pthread_detach en el hilo para separarlo explícitamente. Es un comportamiento indefinido, generalmente un error, llamar a pthread_detach en un subproceso ya separado. Si bien un subproceso puede llamar a pthread_detach en sí mismo, esto generalmente confunde o da como resultado un error en un subproceso que luego espera poder llamar a pthread_join en el subproceso.

    Esto establece que el hilo simplemente desaparecerá, en lugar de permanecer en un estado que se puede unir.

Con respecto a los procesos, y los grupos de procesos:

  • Un hilo separado es análogo a un líder de grupo de procesos. Un líder de grupo de procesos no es recuperable por un proceso principal, ya que el proceso principal está en otro grupo de procesos.
  • Un hilo que se puede unir es análogo a un proceso hijo en el grupo de procesos del padre, para el cual el padre es el líder del grupo de procesos, y el proceso hijo es simplemente un proceso dentro del grupo.

Una llamada a pthread_join bloqueará el subproceso de llamada hasta que el subproceso de destino pueda unirse, o hasta que el proceso salga, o hasta que se llame a pthread_cancel en el subproceso bloqueado (es un punto de cancelación).

Por lo tanto, un pthread_join es análogo a una llamada a waitpid con un valor pid positivo, para no coincidir con cualquier comodín y un valor de argumento de options de 0, para realizar el bloqueo de la llamada. No hay que esperar para pthread_join través de un comodín – para cualquier subproceso que sale, en lugar de un subproceso que sale, a diferencia de waitpid .

Aquí hay unos ejemplos. He omitido la comprobación de errores, para mayor claridad.

Creando un hilo que se puede unir:

  pthread_t new_thread;
	 void * new_thread_exit_status;

	 // Crear un hilo con atributos por defecto;  la amenaza
	 // comienza la ejecución en la función 'my_thread_main'
	 // lo que no tiene argumento
	 pthread_create (& new_thread, NULL, my_thread_main, 0);
 ...
	 // Únete al hilo creado previamente, cosechando la salida.
	 // estado en un valor;  típicamente este es un int
	 pthread_join (new_thread, & new_thread_exit_status);

Creando un hilo separado (método # 1):

  pthread_t new_thread;
	 pthread_attr_t new_thread_attr;

	 // Configurar el atributo separado
	 pthread_attr_init (& new_thread_attr);
	 pthread_attr_setdetachstate (& new_thread_attr, PTHREAD_CREATE_DETACHED);

	 // Crear un hilo con atributo separado;  la amenaza
	 // comienza la ejecución en la función 'my_thread_main'
	 // lo que no tiene argumento
	 pthread_create (& new_thread, & new_thread_attr, my_thread_main, 0);
 ...
	 // XXX No puedo llamar a pthread_join en new_thread

Creando un hilo separado (método # 2):

  pthread_t new_thread;

	 // Crear un hilo con atributos por defecto;  la amenaza
	 // comienza la ejecución en la función 'my_thread_main'
	 // lo que no tiene argumento
	 pthread_create (& new_thread, NULL, my_thread_main, 0);
 ...
	 // new_thread se puede unir actualmente ...
 ...
	 // Separar explícitamente new_thread
	 pthread_detach (new_thread);
 ...
	 // XXX No puedo llamar a pthread_join en new_thread

He omitido el caso donde se llama a pthread_detach desde my_thread_main; no solo lo considero propenso a errores, como se dijo anteriormente, sino que también lo considero una violación de API.

Esto se debe a que considero que es un mal diseño poner parte del control del ciclo de vida en el subproceso de creación, y parte de él en el subproceso creado (esta es la parte propensa a errores), y en segundo lugar porque no quiero proporcionar un implementación de my_thread_main de my_thread_main que se requeriría para demostrar los detalles de la implementación del diseño incorrecto.


Es probable que esta sea una respuesta tan completa como la que obtendrá.

Una vez separado, es para siempre