13

我找到了关于 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);
}

再次感谢大家!!:~)

4

3 回答 3

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变量,确保线程被告知挂起,被告知恢复,并检查它是否应该挂起或在保护下保持挂起,使代码线程安全。

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

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

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

于 2012-12-01T19:36:20.797 回答
4

这是正确的方法。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都使用mutex.
  2. 当我们等待条件变量时,我们必须解锁mutex以允许其他线程将项目添加到队列中,当它被更改时,我们必须mutex再次锁定以实际检查队列。这正是这样pthread_cond_signal做的。
于 2012-12-01T19:11:28.627 回答
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()该线程挂起线程并解锁互斥锁(从而允许其他线程获取锁并可能将对象转换为ready()状态)。

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

为了确保以原子方式正确完成上述操作(解锁/挂起/恢复/锁定),必须由 pthread_cond_wait().

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

不。

还是这是暂停pthread的正确方法?

和其他我想的一样好。

于 2012-12-01T19:14:15.657 回答