C++ PThread初学者-启动、同步、停止工作线程
我有以下管理人员情况:C++ PThread初学者-启动、同步、停止工作线程,c++,synchronization,pthreads,backgroundworker,C++,Synchronization,Pthreads,Backgroundworker,我有以下管理人员情况: class Manager { private: pthread_attr_t workerSettings; pthread_t worker; pthread_cond_t condition; pthread_mutex_t mutex; bool workerRunning; static void* worker_function(void* args) { Manager* manager =
class Manager {
private:
pthread_attr_t workerSettings;
pthread_t worker;
pthread_cond_t condition;
pthread_mutex_t mutex;
bool workerRunning;
static void* worker_function(void* args) {
Manager* manager = (Manager*)args;
while(true) {
while(true) {
pthread_mutex_lock(&manager->mutex);
if(/* new data available */)
{
/* copy new data from shared to thread memory */
pthread_mutex_unlock(&manager->mutex);
}
else
{
pthread_mutex_unlock(&manager->mutex);
break;
}
/* process the data in thread memory */
pthread_mutex_lock(&manager->mutex);
/* copy results back to shared memory */
pthread_mutex_unlock(&manager->mutex);
}
pthread_mutex_lock(&manager->mutex);
// wait for new data to arrive
while(manager->workerRunning && !/* new data available*/)
pthread_cond_wait(&manager->condition, &manager->mutex);
// check if we should continue running
if(!manager->workerRunning)
{
pthread_mutex_unlock(&manager->mutex);
break;
}
pthread_mutex_unlock(&manager->mutex);
}
pthread_exit(NULL);
return NULL; // just to avoid the missing return statement compiler warning
}
public:
Manager() : workerRunning(true) {
pthread_cond_init(&condition, NULL);
pthread_mutex_init(&mutex, NULL);
pthread_attr_init(&workerSettings);
pthread_attr_setdetachstate(&workerSettings, PTHREAD_CREATE_JOINABLE);
pthread_create(&worker, &workerSettings, worker_function, (void*)this);
}
// this *may* be called repeatedly or very seldom
void addData(void) {
pthread_mutex_lock(&mutex);
/* copy new data into shared memory */
pthread_cond_signal(&condition);
pthread_mutex_unlock(&mutex);
}
~Manager()
{
// set workerRunning to false and signal the worker
pthread_mutex_lock(&mutex);
workerRunning = false;
pthread_cond_signal(&condition);
pthread_mutex_unlock(&mutex);
// wait for the worker to exit
pthread_join(worker, NULL);
// cleanup
pthread_attr_destroy(&workerSettings);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&condition);
}
};
在几个地方,我不完全确定这一点:
- Manager在其构造函数中生成新线程这一事实是否被认为是一种不好的做法?(我将只有一个管理器对象,因此我想这应该可以)
- 关于pthread_退出,我在很多教程中都看到了,但我不太明白为什么它会出现?我不能简单地返回函数以退出线程吗?我还认为返回NULL是死代码,但gcc在它丢失时发出警告,因为它显然无法知道pthread_exit在该点上已经杀死了线程
- 关于构造函数-我可以在生成线程后立即销毁thread attr对象(workerSettings),还是必须在线程的整个生命周期内保持有效
- 关于析构函数:这是正确的方法吗
- 您有经验的眼睛是否看到任何同步问题
我可能建议使用pthread_testcancel(),在线程中引入显式取消点,并在控制线程中发出pthread_cancel()+pthread_join()(应返回pthread_cancelled),以停止子线程,而不是同步变量workerRunning。当然,如果它适用于您的情况。您应该在
pthread\u cond\u wait
返回后立即检查新数据,如果没有新数据,请再次等待。如果您得到了一个虚假的唤醒(可以将其视为内核意外地将一些沉重的东西从楼梯上摔下来而将您唤醒),那么可能会发生这种情况,最好立即等待,而不是更改工作等待
然后在再次等待之前解锁并重新锁定互斥锁两次
RAII锁类型将使代码更加干净:
while(true) {
while(true) {
{
scoped_lock l(&manager->mutex);
if(/* new data available */)
{
/* copy new data from shared to thread memory */
}
else
break;
}
/* process the data in thread memory */
scoped_lock l(&manager->mutex);
/* copy results back to shared memory */
}
scoped_lock l(&manager->mutex);
// check if we should continue running
if(!manager->workerRunning)
break;
// wait for new data to arrive
manager->workerWaiting = true;
while (!/* new data available */)
pthread_cond_wait(&manager->condition, &manager->mutex);
manager->workerWaiting = false;
}
按照Oleg的建议使用pthread\u cancel
,可以进一步简化它
在对代码进行编辑以处理虚假唤醒后,如果使用RAII并对其进行重组,则会变得更简单:
while(true)
{
{
scoped_lock l(&manager->mutex);
// wait for new data to arrive
while(manager->workerRunning && !/* new data available*/)
pthread_cond_wait(&manager->condition, &manager->mutex);
// check if we should continue running
if(!manager->workerRunning)
break;
/* copy new data from shared to thread memory */
}
/* process the data in thread memory */
scoped_lock l(&manager->mutex);
/* copy results back to shared memory */
}
return NULL;
如果没有作用域锁定之类的功能,如果/*将新数据从共享内存复制到线程内存*/
或/*处理线程内存中的数据*/
引发异常,会发生什么情况?你永远不会解锁互斥锁
RAII类型可以简单到:
struct scoped_lock {
explicit scoped_lock(pthrad_mutex_t* m) : mx(m) {
pthread_mutex_lock(mx);
}
~scoped_lock() { pthread_mutex_unlock(mx); }
private:
pthread_mutex_t* mx;
scoped_lock(const scoped_lock&);
scoped_lock operator=(const scoped_lock&);
};
关于你的第二点,是的,从一个线程返回就足够了。您实际上不需要
pthread\u exit
,除非您需要从调用的函数中退出线程。好的,谢谢。我错过了什么交易吗?我不喜欢pthread_t对象在任何地方都没有被销毁的事实。它应该被pthread_join
调用“销毁”。忙碌等待永远都是不正常的,而且不清楚它在这段代码中的预期用途。为什么要在调用pthread\u join
之前让工作线程进入某种特定状态?我希望工作线程挂起在pthread\u cond\u wait调用中,因为否则它可能会错过信号并导致死锁(工作线程挂起在pthread\u cond\u wait中,主线程挂起在pthread\u join中)编辑:我想我可以添加另一个if(workerRunning)在pthread_cond_wait调用修复此问题之前..非常感谢您的回答,我不知道取消线程的可能性。这是比我使用的两个布尔更优雅的解决方案。总之,我在等待信号(互斥被解锁)之前在子线程中执行pthread_testcancel(),然后在dtor中发出pthread_cancel,然后发出一个信号(周围有lock/unlock)如果子线程当前正在等待,然后继续加入子线程;pthread_cancel(tid);pthread_join(tid和status);在子线程中(而不是“检查是否应该继续运行”下的代码):pthread_testc