首页 > 解决方案 > 在线程中等待退出条件的正确方法

问题描述

因此,阅读本主题,退出的常用方法是使用标志。我的问题是,如何处理等待?假设线程仅每 30 秒运行一次,您将如何正确等待这 30 秒?

使用sem_timedwait()并不理想,因为它依赖于系统时钟,对时钟的任何更改都会严重影响您的应用程序。本主题解释了如何使用条件变量。问题是,它依赖于互斥体。您不能在信号处理程序中安全地使用pthread_mutex_lock()pthread_mutex_unlock() 。所以就我上面 30 年代的例子来说,如果你想立即退出,谁在处理互斥锁解锁?

我的猜测是另一个线程,其唯一目的是检查退出标志,如果为真,它将解锁互斥锁。但是,那条线是什么样的?只是坐在那里不断检查旗帜不是浪费资源吗?例如,您会使用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;
}

标签: cmultithreadingpthreads

解决方案


解决方案很简单。与其让第一个线程阻塞pthread_join,不如阻塞该线程等待信号。这将确保可以同步处理 SIGINT。

您需要一个受互斥锁保护的全局结构。它应该计算未完成线程的数量以及是否请求关闭。

当一个线程完成时,让它获取互斥锁,减少未完成线程的数量,如果它为零,则发送一个SIGINT. 主线程可以循环等待信号。如果是因为线程数变为零,则让进程终止。如果它来自外部信号,则设置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);
}

推荐阅读