Отдельный поток не завершится, хотя он запускает pthread_exit?

Я уже несколько дней имею дело с проблемой в пуле потоков. Я пробовал все виды разных вещей, но я не могу решить эту проблему. Я сделал простую версию, которая воспроизводит проблему.

Код:

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h> 


struct bsem_t bsem;
pthread_t threads[2];


/* Binary semaphore */
typedef struct bsem_t {
    pthread_mutex_t mutex;
    pthread_cond_t   cond;
    int v;
} bsem_t;

void bsem_post(bsem_t *bsem) {
    pthread_mutex_lock(&bsem->mutex);
    bsem->v = 1;
    pthread_cond_broadcast(&bsem->cond);
    pthread_mutex_unlock(&bsem->mutex);
}

void bsem_wait(bsem_t *bsem) {
    pthread_mutex_lock(&bsem->mutex);
    while (bsem->v != 1) {
        pthread_cond_wait(&bsem->cond, &bsem->mutex);
    }
    bsem->v = 0;
    pthread_mutex_unlock(&bsem->mutex);
}


/* Being called by each thread on SIGUSR1 */
void thread_exit(){
    printf("%u: pthread_exit()\n", (int)pthread_self());
    pthread_exit(NULL);
}


/* Startpoint for each thread */
void thread_do(){

    struct sigaction act;
    act.sa_handler = thread_exit;
    sigaction(SIGUSR1, &act, NULL);

    while(1){
        bsem_wait(&bsem); // Each thread is blocked here
        puts("Passed semaphore");
    }

}


/* Main */
int main(){

    bsem.v = 0;

    pthread_create(&threads[0], NULL, (void *)thread_do, NULL);
    pthread_create(&threads[1], NULL, (void *)thread_do, NULL);
    pthread_detach(threads[0]);
    pthread_detach(threads[1]);
    puts("Created threads");

    sleep(2);

    pthread_kill(threads[0], SIGUSR1);
    pthread_kill(threads[1], SIGUSR1);

    puts("Killed threads");

    sleep(10);

    return 0;
}

Что делает код, так это создает два потока. Оба потока ожидают двоичного семафора (bsem_wait). Затем, пока они ждут, я отправляю SIGUSR1 сигнал к обоим в результате pthread_exit() выполняется в каждом потоке. На моем терминале видно, что все идет по плану..

Выход:

Created threads
Killed threads
2695145216: pthread_exit()
2686752512: pthread_exit()

Эта проблема

Хотя вывод кажется правильным, используя pstree показывает, что только один из двух потоков умирает. Другой поток остается активным до завершения всей программы. Почему это?


Обновить

Замена моего собственного двоичного семафора обычным семафором, кажется, решает эту проблему без видимой причины.

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h> 
#include <semaphore.h> 

sem_t sem;
pthread_t threads[2];


/* Caller thread will exit */
void thread_exit(){
    printf("%u: pthread_exit()\n", (int)pthread_self());
    pthread_exit(NULL);
}


/* Startpoint for each thread */
void* thread_do(){

    struct sigaction act;
    act.sa_handler = thread_exit;
    sigaction(SIGUSR1, &act, NULL);

    while(1){
        sem_wait(&sem); // Each thread is blocked here
        puts("Passed semaphore");
    }

}


/* Main */
int main(){

    sem_init(&sem, 0, 0); // Normal semaphore

    pthread_create(&threads[0], NULL, thread_do, NULL);
    pthread_create(&threads[1], NULL, thread_do, NULL);
    pthread_detach(threads[0]);
    pthread_detach(threads[1]);
    puts("Created threads in pool");

    sleep(2);

    //PROBLEM
    pthread_kill(threads[0], SIGUSR1);
    pthread_kill(threads[1], SIGUSR1);

    puts("Destroyed pool");

    sleep(10);

    return 0;
}

2 ответа

Вы не можете добраться отсюда

Функция pthread_exit() не указана в "функциях безопасной передачи сигналов" справочной страницы signal(7). Перепишите ваш код, чтобы вызов pthread_exit находился вне обработчика сигнала.

Таким образом, проблема казалась тупиковой!

Проблема в том, что каждый поток ожидает внутри bsem_wait бинарного семафора в разных местах:

void bsem_wait(bsem_t *bsem) {
    pthread_mutex_lock(&bsem->mutex); // THREAD 2 BLOCKED HERE
    while (bsem->v != 1) {
        pthread_cond_wait(&bsem->cond, &bsem->mutex); // THREAD 1 WAITING HERE
    }
    bsem->v = 0;
    pthread_mutex_unlock(&bsem->mutex);
}

Нить 1 в этом случае является самой быстрой. Поток 2 медленнее. Когда я запускаю сигнал на удаление потока, ожидающий поток разблокируется и завершается, как и ожидалось. Проблема в том, что он никогда не разблокирует мьютекс. Таким образом, заблокированная нить (2) остается заблокированной навсегда. По какой-то причине поток не будет прерван, потому что он ожидает мьютекс.

Просто добавление разблокировки перед выходом решает проблему:

void thread_exit(){
    printf("%u: pthread_exit()\n", (int)pthread_self());
    pthread_mutex_unlock(&mutex); //  NEW CODE
    pthread_exit(NULL);
}

Это, конечно, взломать, чтобы продемонстрировать, что происходит, и его не следует использовать. Я собираюсь последовать совету Джейсена, избавиться от обработчика сигналов и решить его другим способом. А именно я должен заверить, что поток проходит через весь bsem_wait!

Другие вопросы по тегам