暂停和恢复pthreads的最佳解决方案是什么?

时间:2012-12-01 19:00:58

标签: c++ pthreads posix

我找到了关于pthreads的以下主题(here),但有很多很好的解决方案。

我想知道以下代码是否有效,如果是,为什么使用相同的锁来调用pthread_cond_wait以及访问它然后立即解锁:

void suspendMe()
{
    pthread_mutex_lock(&m_SuspendMutex);
    pthread_cond_wait(&m_ResumeCond, &m_SuspendMutex);
    pthread_mutex_unlock(&m_SuspendMutex);
}

在这里使用2个单独的互斥锁会不会更好,或者这是暂停pthread的正确方法吗?

提前致谢!

修改

非常棒的回复,谢谢大家。 还有一个相关的问题。既然我想在另一个函数中单独恢复一个线程,那么恢复它会更合适吗?

void suspendMe()
{
    pthread_mutex_lock(&m_SuspendMutex);
    pthread_cond_wait(&m_ResumeCond, &m_SuspendMutex);
}
void resumeMe()
{
    pthread_cond_signal(&m_ResumeCond);
    pthread_mutex_unlock(&m_SuspendMutex);
}

再次感谢大家! :〜)

3 个答案:

答案 0 :(得分:8)

实际上,此代码不是线程安全的。互斥体实际上并没有保护任何东西,使隐含的谓词容易受到竞争条件的影响。

看看这段代码 - 互斥保护是什么?什么保护暂停/恢复状态?

void suspendMe()
{
    pthread_mutex_lock(&m_SuspendMutex);
    pthread_cond_wait(&m_ResumeCond, &m_SuspendMutex);
}
void resumeMe()
{
    pthread_cond_signal(&m_ResumeCond);
    pthread_mutex_unlock(&m_SuspendMutex);
}

这是正确的:

void suspendMe()
{ // tell the thread to suspend
    pthread_mutex_lock(&m_SuspendMutex);
    m_SuspendFlag = 1;
    pthread_mutex_unlock(&m_SuspendMutex);
}
void resumeMe()
{ // tell the thread to resume
    pthread_mutex_lock(&m_SuspendMutex);
    m_SuspendFlag = 0;
    phtread_cond_broadcast(&m_ResumeCond);
    pthread_mutex_unlock(&m_SuspendMutex);
}
void checkSuspend()
{ // if suspended, suspend until resumed
    pthread_mutex_lock(&m_SuspendMutex);
    while (m_SuspendFlag != 0) pthread_cond_wait(&m_ResumeCond, &m_SuspendMutex);
    pthread_mutex_unlock(&m_SuspendMutex);
}

线程应该在可以暂停的安全点调用checkSuspend。其他线程可以调用suspendMeresumeMe来暂停/恢复线程。

请注意,现在互斥锁保护m_SuspendFlag变量,确保线程被告知暂停,告知恢复,并检查它是否应该暂停或保持暂停状态,使代码线程安全。< / p>

  

在这里使用2个单独的互斥锁会不会更好,或者这是暂停pthread的正确方法吗?

使用两个互斥锁会破坏条件变量的整个点。它们工作的整个机制是你可以检查是否有你应该等待的东西然后原子地等待它,而不是在你等待或必须释放锁然后等待时持有锁。如果在等待时按住锁定,其他任何线程如何改变状态?如果您释放锁定然后等待,如果您错过了状态更改会发生什么?

顺便说一句,暂停或恢复线程几乎没有意义。如果您觉得需要从外部暂停一个线程,那只表示您编写了线程以执行您实际上不希望它执行的操作。关于暂停或恢复线程的问题通常表明线程编程的心理模型不正确。一个线程可能需要等待某些东西,但它不应该从外部“暂停”,因为它应该已经知道它自己的编码什么时候它不应该做一些特定的工作。

答案 1 :(得分:3)

这是正确的方法。 pthread_cond_wait 解锁 m_SuspendMutex,然后等待m_ResumeCond,然后再次锁定m_SuspendMutex

它以这种方式工作的原因是因为条件变量用于表示某个状态的变化,并且由于该状态是共享的,因此必须在某个线程访问它时锁定它。例如。考虑实施一系列事件:

T get_item()
{
    pthread_mutex_lock(&mutex);
    while(qu.empty())
        pthread_cond_wait(&cond, &mutex);
    T ret = qu.front();
    qu.pop();
    pthread_mutex_unlock(&mutex);
    return ret; // we got an item from a queue
}

void add_item(T x)
{
    pthread_mutex_lock(&mutex);
    qu.push(x);
    pthread_mutex_unlock(&mutex);
    pthread_cond_signal(&cond);
}

请注意:

  1. 使用qu
  2. 同步对队列mutex的所有访问权限
  3. 当我们等待条件变量时,我们必须解锁mutex以允许其他线程将项添加到队列中,并且当它被更改时我们必须再次锁定mutex实际检查队列。这正是pthread_cond_signal的作用。

答案 2 :(得分:1)

  

我想知道以下代码是否有效

  

如果是这样,为什么使用相同的锁来调用pthread_cond_wait以及访问它然后立即解锁:

pthread_cond_wait()需要锁定的互斥锁。这是因为使用条件变量的方式。通常它们的使用方式如下:

pthread_mutex_lock(&m_SuspendMutex);
while (!ready())
{
    pthread_cond_wait(&m_ResumeCond, &m_SuspendMutex);
}

// Modify the resources protexted by the lock.
pthread_mutex_unlock(&m_SuspendMutex);

所以你获得了锁,说你要修改受锁保护的资源。但是如果对象不在ready()状态,那么你用pthread_cond_wait()挂起你的线程,挂起线程并解锁互斥锁(从而允许其他线程获取锁并可能将对象转换为{ {1}}州)。

当达到就绪状态时,发出ready()信号,并从暂停中释放等待线程。但在允许退出m_ResumeCond之前,它必须重新获取pthread_cond_wait()上的锁,以确保它是修改受互斥锁保护的资源的唯一线程。

要确保以原子方式正确完成上述操作(解锁/暂停/恢复/锁定)必须由m_SuspendMutex完成。

  

在这里使用2个单独的互斥锁会不会更好

没有

  

或者这是暂停pthread的正确方法吗?

和其他任何我想的一样好。