Вот не совсем минимальная программа MCVE ( Minimal, Complete, Verifiable Example ), тесно связанная с тем, что показано в вопросе:
#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static int join = 1;
static void *th2(void *args)
{
printf("%s: %d (%p)\n", __func__, *(int *)args, args);
sleep(1);
printf("0x%X\n", *(int *)args);
fflush(stdout);
pthread_exit(NULL);
}
static void *th1(void *args)
{
assert(args == NULL);
pthread_t tid;
int var = 10;
printf("%s: %d (%p)\n", __func__, var, (void *)&var);
pthread_create(&tid, NULL, th2, &var);
if (join)
pthread_join(tid, NULL);
pthread_exit(NULL);
}
/*---*/
static void *th4(void *args)
{
printf("%s: %d (%p)\n", __func__, *(int *)args, args);
sleep(1);
printf("0x%X\n", *(int *)args);
fflush(stdout);
pthread_exit(NULL);
}
static void *th3(void *args)
{
assert(args == NULL);
pthread_t tid;
int *var = malloc(sizeof *var);
*var = 10;
printf("%s: %d (%p)\n", __func__, *var, (void *)var);
pthread_create(&tid, NULL, th4, var);
if (join)
{
pthread_join(tid, NULL);
free(var);
}
/* else leak memory for var */
pthread_exit(NULL);
}
int main(int argc, char **argv)
{
pthread_t t1;
pthread_t t3;
if (argc > 1 && argv[argc] == NULL)
join = 0;
printf("%s pthread_join() on sub-threads\n", join ? "Using" : "Not using");
printf("launch 1\n");
pthread_create(&t1, NULL, th1, NULL);
pthread_join(t1, NULL);
printf("launch 3\n");
pthread_create(&t3, NULL, th3, NULL);
pthread_join(t3, NULL);
printf("finished\n");
return 0;
}
Он настроен так, что если передан аргумент командной строки, дочерние потоки th1()
и th3()
не выполняют pthread_join()
до выхода; если аргумент не передан, они ждут.
При компиляции как pth19
и запуске (на Mac с MacOS 10.14.2 Mojave, с использованием GCC 8.2.0) я получаю:
$ pth19
Using pthread_join() on sub-threads
launch 1
th1: 10 (0x70000bda2f04)
th2: 10 (0x70000bda2f04)
0xA
launch 3
th3: 10 (0x7fa0a9500000)
th4: 10 (0x7fa0a9500000)
0xA
finished
$ pth19 1
Not using pthread_join() on sub-threads
launch 1
th1: 10 (0x70000690ff04)
Segmentation fault: 11
$
При использовании с вызовами pthread_join()
работает правильно и как ожидалось.
Когда объединения не указаны, происходит сбой кода, что является одним из проявлений «неопределенного поведения». Если вы не присоединяетесь к потокам th2
и th4
, потоки th1
и th3
могут оставлять другим доступ к данным, которые больше не действительны. (Конечно, выделенная память не была освобождена в оригинале, но сбой происходил до выделения памяти.)
Будьте внимательны, чтобы потоки обращались только к действительным данным.
Не пытайтесь делиться данными между потоками, как это; Вы берете работу, которая уже трудна (правильное программирование потоков трудно), и делаете ее еще сложнее.