Правильный метод ожидания условий выхода в потоках - PullRequest
1 голос
/ 23 марта 2020

Итак, читая это топи c, обычным способом выхода будет использование флага. У меня вопрос, как обрабатывается ожидание? Скажем, поток должен запускаться только каждые 30 секунд, как бы вы правильно подождали эти 30 секунд?

Использование sem_timedwait () не идеально, так как зависит от системных часов и любых изменений в часы могут серьезно повлиять на ваше приложение. В этой теме c объясняется использование условных переменных. Проблема в том, что он опирается на мьютекс. Вы не можете безопасно использовать pthread_mutex_lock () и pthread_mutex_unlock () в обработчике сигналов. Итак, с точки зрения моего примера выше 30-х годов, если вы хотите выйти немедленно, кто обрабатывает мьютексную разблокировку?

Я думаю, что другой поток, единственной целью которого является проверка флага выхода, и если true, то это будет разблокировать мьютекс. Тем не менее, что это за нить? Разве это не пустая трата ресурсов, чтобы просто сидеть и постоянно проверять флаг? Не могли бы вы использовать sleep () и проверять, например, каждые 1 с?

Не думаю, что мои предположения правильные. Это кажется очень неэффективным, и я сталкиваюсь с подобным вопросом типа «как мне ждать». Я чувствую, что что-то упустил, но мой поиск приводит к темам, похожим на то, что я связал, где говорится о флагах, но ничего не ждет.

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>

pthread_mutex_t my_mutex;
volatile sig_atomic_t exitRequested = 0;

void signal_handler(int signum) {
    exitRequested = 1;
}

bool my_timedwait(pthread_mutex_t *mutex, int seconds) {
    pthread_condattr_t attr;
    pthread_condattr_init(&attr);
    pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);

    pthread_cond_t cond;
    pthread_cond_init(&cond, &attr);

    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    ts.tv_sec += seconds;

    int status = pthread_cond_timedwait(&cond, mutex, &ts);
    if (status == 0) {
        return false; // mutex unlocked
    }

    if ((status < 0) && (status != ETIMEDOUT)) {
        // error, do something
        return false;
    }

    return true; // timedout
}

void *exitThread(void *ptr) {
    // constant check???
    while (1) {
      if (exitRequested) {
          pthread_mutex_unlock(&my_mutex);
          break;
      }
    }
}

void *myThread(void *ptr) {
    while (1) {
        // do work
        printf("test\n");

        // wait and check for exit (how?)
        if (!my_timedwait(&my_mutex, 30)) {
            // exiting
            break;
        }
    }
}

int main(void) {
    // init and setup signals
    struct sigaction sa;
    sa.sa_handler = signal_handler;
    sigaction(SIGINT, &sa, NULL);

    // init the mutex and lock it
    pthread_mutex_init(&my_mutex, NULL);
    pthread_mutex_lock(&my_mutex);

    // start exit thread
    pthread_t exitHandler;
    pthread_create(&exitHandler, NULL, exitThread, NULL);

    // start thread
    pthread_t threadHandler;
    pthread_create(&threadHandler, NULL, myThread, NULL);

    // wait for thread to exit
    pthread_join(threadHandler, NULL);
    pthread_join(exitHandler, NULL);

    return EXIT_SUCCESS;
}

1 Ответ

1 голос
/ 23 марта 2020

Решение простое. Вместо того чтобы иметь первый блок потока в pthread_join, блокируйте этот поток в ожидании сигналов. Это обеспечит синхронную обработку SIGINT.

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

Когда поток завершает работу, он получает мьютекс, уменьшает число ожидающих потоков и, если он равен нулю, отправляет * 1006. *. Основной поток может l oop в ожидании сигнала. Если это из числа потоков, идущих в ноль, позвольте процессу завершиться. Если это от внешнего сигнала, установите флаг shutdown, передайте переменную условия, разблокируйте мьютекс и продолжайте ждать, пока счетчик потоков не достигнет нуля.

Вот начало:

pthread_mutex_t my_mutex; // protects shared state
pthread_cond_t my_cond;   // allows threads to wait some time
bool exitRequested = 0;   // protected by mutex
int threadsRunning = 0;   // protected by mutex
pthread_t main_thread;    // set in main

bool my_timedwait(int seconds)
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    ts.tv_sec += seconds;

    pthread_mutex_lock (&my_mutex);
    while (exitRequested == 0)
    {
        int status = pthread_cond_timedwait(&my_cond, &my_mutex, &ts);
        if (status == ETIMEDOUT) // we waited as long as supposed to
            break;
    }

    bool ret = ! exitRequested;
    pthread_mutex_unlock (&my_mutex);

    return ret; // timedout
}

bool shuttingDown()
{
    pthread_mutex_lock (&my_mutex);
    bool ret = exitRequested;
    pthread_mutex_unlock (&my_mutex);
    return ret;
}

void requestShutdown()
{
    // call from the main thread if a SIGINT is received
    pthread_mutex_lock (&my_mutex);
    exitRequested = 1;
    pthread_cond_broadcast (&my_cond);
    pthread_mutex_unlock (&my_mutex);
}

void threadDone()
{
    // call when a thread is done
    pthread_mutex_lock (&my_mutex);
    if (--threadsRunning == 0)
        pthread_kill(main_thread, SIGINT); // make the main thread end
    pthread_mutex_unlock (&my_mutex);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...