Linux 多线程服务端编程笔记(第二章)

Q1:

在编写一个自己的MutexLock,MutexLockGuard,Condition_var时,为何pthread的condition的wait及signal操作需要传入mutex?

A1:

具体的可见这个帖子:c - Does pthread_cond_wait(&cond_t, &mutex); unlock and then lock the mutex? - Stack Overflow

必须知道条件变量并不需要互斥锁来保护,网络上说需要保护条件变量的都是胡扯.回答中这样说道:

The condition variable doesn't need mutual exclusion protection; the predicate data does.

那为什么在wait和signal的时候总是在外层包一层mutex呢?
那是因为我们必须要保护谓词(predicate),我们使用条件变量的时候是要和谓词一起用的,要是我们想实现这样一个功能:一旦资源数大于0就进行处理,我们可以有下面的代码(这其实也是信号量的实现原理)

extern int rc_count; // 表示资源值
pthread_mutex_t mutex;//是为了保护rc_count,即rc_count就是谓词
pthread_cond_t cond;

pthread_mutex_lock(&mutex);
while(rc_count <= 0)
{
  pthread_cond_wait(&cond,&mutex);//只有当资源量大于0时才可能跳出循环.
}
pthread_mutex_unlock(&mutex);

简单来说就是condition的wait和signal是一种简单的信号机制,在wait时,就将当前线程阻塞,并且还要把负责将mutex打开,否则遵循mutex机制的其他乖孩子线程无法更改谓词,和发出signal了.这样wait的线程就会一直阻塞,无法进行下一步操作.而在wait退出的时候,会负责将mutex锁上,从而wait的线程从wait刚退出就自动拥有这个mutex所保护的谓词.帖子中说道:

Never change, nor check, the predicate condition unless the mutex is locked. Ever.

注意,我这里所说的一直阻塞其实只会发生在谓词改变不了的时候.我们一般在signal的线程中采用下面的两种规范写法:

pattern1

pthread_mutex_lock(&mtx);
TODO: change predicate state here as needed.
pthread_mutex_unlock(&mtx);
pthread_cond_signal(&cv);

pattern2

pthread_mutex_lock(&mtx);
TODO: change predicate state here as needed.
pthread_cond_signal(&cv);
pthread_mutex_unlock(&mtx);

想想如果我们的线程都是乖孩子,意味着只有获取锁才能改变谓词,这样的话如果因为某些粗心的程序员写出了下面的代码:

some code
pthread_cond_signal(&cv)//错误添加了这一行
some code
pthread_mutex_lock(&mtx);
TODO: change predicate state here as needed.
pthread_cond_signal(&cv);
pthread_mutex_unlock(&mtx);

由于我们wait端一般是这样写的:

while(rc_count <= 0)
{
  pthread_cond_wait(&cond,&mutex);
}

这样的话,即使错误信号发出,只要谓词不满足条件一样是线程阻塞.

下面是一个例子:

int WaitForPredicate()
{
    // lock mutex (means:lock access to the predicate)
    pthread_mutex_lock(&mtx);

    // we can safely check this, since no one else should be 
    // changing it unless they have the mutex, which they don't
    // because we just locked it.
    while (!predicate)
    {
        // predicate not met, so begin waiting for notification
        // it has been changed *and* release access to change it
        // to anyone wanting to by unlatching the mutex, doing
        // both (start waiting and unlatching) atomically
        pthread_cond_wait(&cv,&mtx);

        // upon arriving here, the above returns with the mutex
        // latched (we own it). The predicate *may* be true, and
        // we'll be looping around to see if it is, but we can
        // safely do so because we own the mutex coming out of
        // the cv-wait call. 
    }

    // we still own the mutex here. further, we have assessed the 
    //  predicate is true (thus how we broke the loop).

    // take whatever action needed. 

    // You *must* release the mutex before we leave. Remember, we
    //  still own it even after the code above.
    pthread_mutex_unlock(&mtx);
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容