Кто 4-й поток при использовании 3 потоков для обработки сигналов - PullRequest
1 голос
/ 02 мая 2019

Я испытываю реакцию сигналов при использовании с потоками. Я знаю, что семейные функции printf не являются сигнальными безопасными функциями, но для простоты, которую я использую. Ниже сказано, что

Когда сигнал доставляется многопоточному процессу, который установил обработчик сигнала, ядро ​​произвольно выбирает один поток в процессе, чтобы доставить сигнал и вызывает обработчик в этом потоке. Такое поведение обусловлено поддержанием традиционная семантика сигнала. Это не имеет смысла для процесс выполнения действий по обработке сигналов несколько раз в ответ на один сигнал.

Чтобы набрать жирный текст, я пишу следующий основной код, но когда я нажимаю Ctrl-C, я получаю действительно другой идентификатор потока, а не созданные. Кто это? Почему я не вижу, что другой поток может поймать сигнал.

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

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;

int count = 0;
int globerr;

void *inc(void *arg) {
    fprintf(stderr, "Thread id = %d\n", (int)pthread_self());
    sleep(2);
    int error;
    for (int i = 0; i < 15; ++i) {
        if (error = pthread_mutex_lock(&mtx)) {
            globerr = error;
            return 0;
        }

        count++;
        printf("thread id = %d, count = %d\n", pthread_self(), count);
        if (count == 20) {

            printf("thread id = %d, SIGNAL count = %d\n", pthread_self(), count);
            pthread_cond_signal(&cv);
        }


        if (error = pthread_mutex_unlock(&mtx)) {
            globerr = error;
            return 0;
        }
    }
}

void* watcher(void *arg) {
    fprintf(stderr, "Thread id = %d\n", (int)pthread_self());
    sleep(2);
    int error;
    if (error = pthread_mutex_lock(&mtx)) {
        globerr = error;
        return 0;
    }

    while (count < 20) {
        printf("watcher thread id = %d, BLOCKING count = %d\n", pthread_self(), count);
        pthread_cond_wait(&cv, &mtx);
        printf("watcher thread id = %d, UNBLOCKING count = %d\n", pthread_self(), count);

    }


    printf("watcher thread id = %d, count = %d\n", pthread_self(), count);

    if (error = pthread_mutex_unlock(&mtx)) {
        globerr = error;
        return 0;
    }

}

static void signal_handler(int sig){
    if (sig == SIGINT)
        printf("Caught signal for Ctrl+C, Thread id = %d\n", (int)pthread_self());

    pthread_cancel(pthread_self());
}

int main(void) {
    struct sigaction sigact;
    sigact.sa_handler = signal_handler;
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = 0;
    sigaction(SIGINT, &sigact, (struct sigaction *)NULL);

    pthread_t t[3];
    for (int i = 0; i < 2; ++i) {
        pthread_create(&t[i], 0, inc, 0);
    }

    pthread_create(&t[2], 0, watcher, 0);

    sleep(3);

    for (int i = 0; i < 3; ++i) {
        pthread_join(t[i], 0);
    }
}

Пример вывода,

MacBook-Pro-2:cmake-build-debug soner$ ./client 
Thread id = 171921408
Thread id = 172457984
Thread id = 172994560
^CCaught signal for Ctrl+C, Thread id = 360719808
^CCaught signal for Ctrl+C, Thread id = 171921408
^CCaught signal for Ctrl+C, Thread id = 172457984
^CCaught signal for Ctrl+C, Thread id = 172994560

Кто такой 360719808?

1 Ответ

1 голос
/ 02 мая 2019

Если вы вызываете pthread_create три раза, ваша программа имеет четыре потока: четвертый - начальный поток , который существовал с начала вашей программы. Вы можете думать об этом как о потоке, процедура которого main.

В такой программе вы должны выделить один поток для обработки сигналов. Вы делаете это в четыре шага:

  1. В main, перед созданием каких-либо потоков, используйте pthread_sigmask для блокировки всех сигналов, кроме тех, которые указывают синхронные фатальные события (SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGSYS и SIGTRAP ). Не устанавливайте обработчики сигналов.
  2. Создать поток с процедурой потока, предназначенной для обработки сигналов.
  3. Этот поток повторяет вызов sigwaitinfo для сигналов, которые вам нужны. Это должно включать, по крайней мере, сигналы, которые указывают на запрос внешнего завершения или приостановки: SIGINT, SIGHUP, SIGPWR, SIGQUIT, SIGTERM, SIGTSTP, SIGXCPU. Если вы используете дочерние процессы, вы также должны включить SIGCHLD.
  4. Другие ваши потоки делают все, что им нужно, не беспокоясь о сигналах. Вероятно, они проводят большую часть своего времени, либо делая вычисления, либо блокируются на select.
...