等待线程退出条件的正确方法
Proper method to wait for exit conditions in threads
所以阅读 ,常见的退出方式是使用标志。我的问题是,如何处理等待?假设线程每 30 秒只到 运行,您将如何正确等待这 30 秒?
在信号处理程序中使用 sem_timedwait() isn't ideal as it relies on the system clock and any change to the clock can severely impact your application. This topic explains using condition variables instead. The problem is, it relies on a mutex. You can't safely use pthread_mutex_lock() and pthread_mutex_unlock()。所以就我上面30s的例子来说,如果你想立即退出,谁在处理互斥量解锁?
我的猜测是另一个线程,其唯一目的是检查退出标志,如果为真,它将解锁互斥体。然而,那个线程是什么样的?只是坐在那里不停地检查标志不是浪费资源吗?例如,您会使用 sleep() 并每隔 1 秒检查一次吗?
我不相信我的猜测是正确的。这似乎非常低效,我 运行 进入类似 "how do I wait" 类型的问题。我觉得我遗漏了一些东西,但我的搜索导致的主题类似于我在其中谈论标志的链接,但没有等待。
#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);
}
所以阅读
在信号处理程序中使用 sem_timedwait() isn't ideal as it relies on the system clock and any change to the clock can severely impact your application. This topic explains using condition variables instead. The problem is, it relies on a mutex. You can't safely use pthread_mutex_lock() and pthread_mutex_unlock()。所以就我上面30s的例子来说,如果你想立即退出,谁在处理互斥量解锁?
我的猜测是另一个线程,其唯一目的是检查退出标志,如果为真,它将解锁互斥体。然而,那个线程是什么样的?只是坐在那里不停地检查标志不是浪费资源吗?例如,您会使用 sleep() 并每隔 1 秒检查一次吗?
我不相信我的猜测是正确的。这似乎非常低效,我 运行 进入类似 "how do I wait" 类型的问题。我觉得我遗漏了一些东西,但我的搜索导致的主题类似于我在其中谈论标志的链接,但没有等待。
#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);
}