c - 在线程中等待退出条件的正确方法
问题描述
因此,阅读本主题,退出的常用方法是使用标志。我的问题是,如何处理等待?假设线程仅每 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;
}
解决方案
解决方案很简单。与其让第一个线程阻塞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);
}
推荐阅读
- aws-lambda - CloudFormation - 无法在 StepFunctions StateMachine 的 DefinitionString 中导入 lambda arn
- javascript - jquery只附加一个ajax响应
- python-3.x - 当我专门让它们工作时,为什么我的线程在睡觉
- php - 如何使用 pdo 获取所有行?
- graph-drawing - 如何在博客中绘制节点和边
- sql - SQL 选择不同的值,其中状态 = MAX
- bash - 删除多个具有上次修改日期条件的 s3 存储桶文件
- ruby - 根据输入参数构建sql查询
- c# - 从一个视图 MVC 中的三个上传控件获取相同的图像
- java - 当我很确定数组已正确定义时出现.class错误