разветвление процесса с несколькими потоками C

#c #pthreads #fork

#c #pthreads #разветвление

Вопрос:

Я следую этому руководству о pthread и fork , но я не понимаю, как работает следующий код. Следующий код взят из https://github.com/angrave/SystemProgramming/wiki/Pthreads,-Part-2:-Usage-in-Practice

 #include <pthread.h>
#include <stdio.h>
#include <unistd.h>

static pid_t child = -2;

void *sleepnprint(void *arg) {
 printf("%d:%s starting up...n", getpid(), (char *) arg);

 while (child == -2) {sleep(1);} /* Later we will use condition variables */

 printf("%d:%s finishing...n",getpid(), (char*)arg);

 return NULL;  
}
int main() {
 pthread_t tid1, tid2;
 pthread_create(amp;tid1,NULL, sleepnprint, "New Thread One");
 pthread_create(amp;tid2,NULL, sleepnprint, "New Thread Two");
 
 child = fork();
 printf("%d:%sn",getpid(), "fork()ing complete");
 sleep(3);
   
 printf("%d:%sn",getpid(), "Main thread finished");
 
 pthread_exit(NULL);
 return 0; /* Never executes */
}
  
 8970:New Thread One starting up...
8970:fork()ing complete
8973:fork()ing complete
8970:New Thread Two starting up...
8970:New Thread Two finishing...
8970:New Thread One finishing...
8970:Main thread finished
8973:Main thread finished
  
  1. Является ли 8970 родительским процессом?
  2. На веб-сайте говорится, что у дочернего процесса был только один поток, значит ли это, что дочерний процесс не sleepnprint работает?
  3. Зачем 8970:fork()ing complete 8973:fork()ing complete печатать раньше 8970:New Thread Two starting up... ? Являются ли порядок потоков и процессов случайным?

Комментарии:

1. Вы не можете изменить переменную (дочернюю) в одном потоке и просмотреть ее в другом потоке без надлежащей синхронизации.

2. Зачем вообще использовать fork(), если потоковая обработка доступна? Это дисфункциональная функция из эпохи до многопоточности.

Ответ №1:

Основной поток (функция main()) создает два потока с помощью функции pthread_create(), но поскольку они являются потоками, они являются частью одного и того же процесса (getpid() возвращает 8970 для основного потока и вторичных потоков). Если вам нужны идентификаторы задач, вызовите gettid() в точке входа потока (вы получите соответственно 8971 и 8972).

Затем отцовский процесс разветвляется, и оба отцовских и дочерних процесса продолжаются в функции main(). Они соответственно отображают свои pid: 8970 и 8973.

Когда многопоточный процесс разветвляется, в дочернем процессе «воспроизводится» только вызывающий поток (другие потоки родительского процесса не разветвляются: дочерний процесс является однопоточным, пока не создаст новые потоки на своей стороне). Итак, в вашем примере дочерний процесс с номером 8973 не имеет двух потоков, созданных в родительском процессе (номер 8970).

Да, все потоки и процессы выполняются параллельно (в любом порядке).

Чтобы проиллюстрировать предыдущее, вот немного улучшенная версия вашей программы:

 #define _GNU_SOURCE  // To get gettid()
#include <pthread.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

static volatile pid_t child = -2;

void *sleepnprint(void *arg)
{
  printf("Process %d, Task %d:%s starting up...n", getpid(), gettid(), (char *) arg);

  // This is not the best way to synchronize threads but this works here:
  // once the main thread returns from fork(), child = pid of child process
  // (i.e. != -2)
  while (child == -2) {sleep(1);} /* Later we will use condition variables */

  printf("Process %d, Task %d:%s finishing...n", getpid(), gettid(), (char*)arg);

  return NULL;  
}

int main() {

  pthread_t tid1, tid2;
  pthread_create(amp;tid1,NULL, sleepnprint, "New Thread One");
  pthread_create(amp;tid2,NULL, sleepnprint, "New Thread Two");
 
  child = fork();

  // In father process: child = child process pid
  // In child process: child = 0

  if (child == 0) {

    // This is the child process

    printf("%d:%sn",getpid(), "Child process finished");

    exit(0);
  }

  // Father process

  printf("%d:%sn",getpid(), "fork()ing complete");
  sleep(3);

  printf("%d:%sn",getpid(), "Main thread finished");

  pthread_exit(NULL);
  return 0; /* Never executes */
}
  

Компиляция и выполнение:

 $ gcc example.c -l pthread
$ ./a.out
Process 6141, Task 6142:New Thread One starting up...
Process 6141, Task 6142:New Thread One finishing...
Process 6141, Task 6143:New Thread Two starting up...
Process 6141, Task 6143:New Thread Two finishing...
6144:Child process finished
6141:fork()ing complete
6141:Main thread finished