Вызвать присоединение к дочернему потоку в основной функции

У меня есть тестовый код:

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

pthread_t th_worker, th_worker2;



void * worker2(void *data) {
    for(int i = 0; i< 1000000; i++){
            printf("thread for worker2----%d\n", i);
            usleep(500);
    }
}
void * worker(void *data){
    pthread_create(&th_worker2, NULL, worker2, data);

    for(int i = 0; i< 100; i++){
            printf("thread for worker-----%d\n", i);
            usleep(500);
    }
}
void join(pthread_t _th){
    pthread_join(_th, NULL);
}

В функции main(), если я вызову join(the_worker2):

int main() {
    char* str = "hello thread";

    pthread_create(&th_worker, NULL, worker, (void*) str);
    /* problem in here */
    join(th_worker2);

    return 1;
}

--> Ошибка сегмента

В противном случае я звоню:

join(the_worker);
join(th_worker2);

---> OK

Почему в приведенном выше случае возникает ошибка ошибки сегмента? Спасибо за помощь !!!


person Thtam    schedule 07.12.2015    source источник


Ответы (1)


Если вы опубликовали весь свой код, у вас есть состояние гонки.

main синхронизировано с началом worker, но нет worker2.

То есть main пытается присоединиться к th_worker2 до того, как worker успел вызвать pthread_create и настроить th_worker2 с действительным [ненулевым] значением.

Таким образом, th_worker2 будет недействительным, пока не завершится второй pthread_create, но для main уже слишком поздно. Он уже получил th_worker2, который имеет значение NULL, и main вызовет ошибку сегментации.

Когда вы добавляете соединение для th_worker, оно работает, потому что оно гарантирует синхронизацию и отсутствие состояния гонки.


Чтобы добиться этой гарантии без соединения, сделайте так:

int
main()
{
    char *str = "hello thread";

    pthread_create(&th_worker, NULL, worker, (void *) str);

    // give worker enough time to properly start worker2
    while (! th_worker2)
        usleep(100);

    /* problem in here */
    join(th_worker2);

    return 1;
}

Еще лучший способ сделать это — добавить дополнительную переменную. При этом первый цикл не нужен [но я оставил его]:

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

int worker_running;
pthread_t th_worker;

int worker2_running;
pthread_t th_worker2;

void *
worker2(void *data)
{

    // tell main we're fully functional
    worker2_running = 1;

    for (int i = 0; i < 1000000; i++) {
        printf("thread for worker2----%d\n", i);
        usleep(500);
    }

    return NULL;
}

void *
worker(void *data)
{

    // tell main we're fully functional
    worker_running = 1;

    pthread_create(&th_worker2, NULL, worker2, data);

    for (int i = 0; i < 100; i++) {
        printf("thread for worker-----%d\n", i);
        usleep(500);
    }

    return NULL;
}

void
join(pthread_t _th)
{
    pthread_join(_th, NULL);
}

int
main()
{
    char *str = "hello thread";

    pthread_create(&th_worker, NULL, worker, (void *) str);

    // give worker enough time to properly start worker2
    // NOTE: this not necessarily needed as loop below is better
    while (! th_worker2)
        usleep(100);

    // give worker2 enough time to completely start
    while (! worker2_running)
        usleep(100);

    /* problem in here (not anymore!) */
    join(th_worker2);

    return 1;
}
person Craig Estey    schedule 08.12.2015