我有以下经理< - >工人情况:
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);
}
};
我在几个地方对此并不完全确定:
最重要的是:
感谢您的帮助!
答案 0 :(得分:3)
你问......
Manager是否在其构造函数中生成一个新线程被认为是一种不好的做法?
在大多数情况下,RAII足以接近对象创建和资源获取。在某些情况下,您可能希望实现延迟资源初始化:首次构造对象时,稍后继续初始化。例如,这可以通过ctor(默认或参数化)和打开/启动例程来实现。虽然您也可以在ctor中执行此操作,并通过在进程堆中分配对象(通过operator new)来实现deffered对象的创建。这取决于您的要求,软件设计考虑因素和企业软件开发标准。 因此,您可以在ctor中创建一个线程,或者可能希望或需要在应用程序/对象生命周期的后期阶段生成它。
pthread_exit
怎么样?
不是必需的。它终止调用线程,使其退出状态可用于任何等待的线程(即通过pthread_join())。当任何线程从其启动例程返回时,会发生对pthread_exit()的隐式调用。基本上,pthread_exit()函数提供类似于exit()的接口,但是基于每个线程(包括取消清理处理程序)。但要注意从取消清除处理程序或TSD(特定于线程的数据区域)中分配的对象的析构函数调用pthread_exit() - 它可能导致不良副作用。
关于构造函数 - 我可以在产生线程后立即销毁线程attr对象(workerSettings),还是必须在线程的整个生命周期内保持有效?
是的,您可以立即销毁它:它不会影响已经创建的线程。
关于析构函数:这是正确的方法吗?
与ctor相同:您可以使用dtor和关闭/停止例程,或者可以在dtor中完成所有操作:取决于您的特定需求(例如,对象可重用性等)。只是让dtor不要扔掉。
您经验丰富的眼睛是否看到任何同步问题?
我建议使用pthread_testcancel(),在线程中引入显式取消点,并在控制线程中发出pthread_cancel()+ pthread_join()(应返回PTHREAD_CANCELED)以停止子线程,而不是同步变量workerRunning 。当然,如果它适用于您的情况。
答案 1 :(得分:2)
一旦pthread_cond_wait
返回,您应该检查新数据,如果没有新数据,请再次等待。如果你得到一个虚假的唤醒,可能会发生这种情况(想想它是因为内核意外地通过在楼梯上放下一些东西而唤醒你),最好立即等待而不是改变workerWaiting
然后解锁和重新锁定mutex再次等待两次。
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;
}
使用pthread_cancel
作为Oleg建议会进一步简化它。
在您编辑代码以处理虚假唤醒之后,如果您使用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;
如果不使用scoped_lock,如果/* copy new data from shared to thread memory */
或/* process the data in thread memory */
抛出异常会发生什么?你永远不会解锁互斥锁。
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&);
};