mutex and condition 推理法

假设有一个数组。

线程1往数组中放数据,线程2读取数据,然后删掉。

一阶段

那么写法应该这样:

读取线程1:
while(true)
  lock(a)
    if(array.length > 0 )
        read();
  unlock(a)

写线程2:
  lock(a)
        write();
  unlock(a)

对于线程1而言,每次需要检查数组是否为空,否则,来判断是否要读。
忙等的方式显然不是特别好的方式。
关于这一点参考文章:
https://blog.csdn.net/flymachine/article/details/10012959

二阶段

想象下存在这么一个,condition的能力。

读取线程1:
while(true)
  wait(cond);
  lock(a)
        read();
  unlock(a)

写线程2:
  lock(a)
        write();
  unlock(a)
  signal(cond)

上面的方式,可以让线程1,等待触发条件,直到条件发生,才运行,而运行前都在阻塞不占用CPU。

三阶段

上面的方式对于只有一个读或只有一个写是没有问题的。
假设不止一个读进程,也不止一个写进程呢?会发生多线程操作个cond条件。所以不得不加个锁。

先明白错

该怎么写?下面这个方式明显是不行的,因为如果读取线程获取到锁以后,就等待了,如果线程2运行的比线程1晚的话,那么就永远无法抢到锁,也就无法发信号量,从而死锁。

读取线程1:
while(true)
  lock(mutex);
  wait(cond);
  unlock(mutex);
  lock(a)
        read();
  unlock(a)

写线程2:
  lock(a)
        write();
  unlock(a);
  lock(mutex);
  signal(cond)
  unlock(mutex);

如何解决?
下面是一种想出来的解决思路:配合非阻塞的锁,以及条件等待,来、无锁的条件等待。

然后知道对

读取线程1:
while(true)
  while(size=0){// 复查条件,因为释放了锁以后,可能多个线程1被唤醒。
    lock(mutex);
    wait(cond,mutex){
          while(!trylock(进入)){//这里确保了每次只有一个读会进入。
              unlock(mutex);//既然已经有别的读线程获取了“进入”锁,那就放弃了,这样也不会阻塞其它的进程。包括其它的读,和其它的写。
              wait(A);//等待进入锁释放的信号,这样不会有忙等的低效率
              lock(mutex);
          }
          unlock(mutex);
          
          开始等待();
          unlock(进入);
          signal(A);//进入锁释放了

          //这个前后结果一样,但是效率上一点点小差别,因为先释放信号量,其它线程唤醒后,尝试获取锁,然后会失败,不过下次再实验的时候就会成功的。后释放,获取的时候至少有一个线程会成功。
          // signal(A);
          // unlock(我已经进来了);
     }
  }
  lock(a)
        read();
  unlock(a)

写线程2:
  lock(a)
        write();
  unlock(a);
  lock(mutex);
  signal(cond)
  unlock(mutex);

标准的用法。其实是因为pthread_cond_wait里面做了unlock 操作和然后又做了lock操作,内部的具体实现不清楚,我能想到的是上面的实现方式。

为什么最后还要外面unlock一次,我理解是两点。
1.为了让函数对称,为了每个lock 都有一个unlock 所以pthread_cond_wait 内部要使用unlock一定也要有对应的lock.
2.lock 后,可能会产生相应的对cond 对修改。这样lock 也是必要的。不过函数内部lock之后,不能unlock ,因为外面会有一个unlock 和他呼应。

In Thread1:
pthread_mutex_lock(&m_mutex);   
pthread_cond_wait(&m_cond,&m_mutex);   
读
pthread_mutex_unlock(&m_mutex);  

 

In Thread2:
pthread_mutex_lock(&m_mutex);   
写
pthread_cond_signal(&m_cond);   
pthread_mutex_unlock(&m_mutex);   

百科上有个例子不错:
https://baike.baidu.com/item/pthread_cond_wait/3011997?fr=aladdin

#include<pthread.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
 
static pthread_mutex_t mtx=PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
 
struct node {
    int n_number;
    struct node *n_next;
} *head=NULL; /*[thread_func]*/
 
/*释放节点内存*/
static void cleanup_handler(void*arg) {
    printf("Clean up handler of second thread.\n");
    free(arg);
    (void)pthread_mutex_unlock(&mtx);
}
 
static void *thread_func(void *arg) {
    struct node*p=NULL;
    pthread_cleanup_push(cleanup_handler,p);
 
    pthread_mutex_lock(&mtx);
    //这个mutex_lock主要是用来保护wait等待临界时期的情况,
    //当在wait为放入队列时,这时,已经存在Head条件等待激活
    //的条件,此时可能会漏掉这种处理
    //这个while要特别说明一下,单个pthread_cond_wait功能很完善,
    //为何这里要有一个while(head==NULL)呢?因为pthread_cond_wait
    //里的线程可能会被意外唤醒,如果这个时候head==NULL,
    //则不是我们想要的情况。这个时候,
    //应该让线程继续进入pthread_cond_wait
    while(1) {
        while(head==NULL) {
            pthread_cond_wait(&cond,&mtx);
        }
        //pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mtx,
        //然后阻塞在等待队列里休眠,直到再次被唤醒
        //(大多数情况下是等待的条件成立而被唤醒,唤醒后,
        //该进程会先锁定先pthread_mutex_lock(&mtx);,
        //再读取资源用这个流程是比较清楚的
        /*block-->unlock-->wait()return-->lock*/
        p=head;
        head=head->n_next;
        printf("Got%dfromfrontofqueue\n",p->n_number);
        free(p);
    }
    pthread_mutex_unlock(&mtx);//临界区数据操作完毕,释放互斥锁
 
    pthread_cleanup_pop(0);
    return 0;
}
 
int main(void) {
    pthread_t tid;
    int i;
    struct node *p;
    pthread_create(&tid,NULL,thread_func,NULL);
    //子线程会一直等待资源,类似生产者和消费者,
    //但是这里的消费者可以是多个消费者,
    //而不仅仅支持普通的单个消费者,这个模型虽然简单,
    //但是很强大
    for(i=0;i<10;i++) {
        p=(struct node*)malloc(sizeof(struct node));
        p->n_number=i;
        pthread_mutex_lock(&mtx);//需要操作head这个临界资源,先加锁,
        p->n_next=head;
        head=p;
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mtx);//解锁
        sleep(1);
    }
    printf("thread1wannaendthecancelthread2.\n");
    pthread_cancel(tid);
    //关于pthread_cancel,有一点额外的说明,它是从外部终止子线程,
    //子线程会在最近的取消点,退出线程,而在我们的代码里,最近的
    //取消点肯定就是pthread_cond_wait()了。
    pthread_join(tid,NULL);
    printf("Alldone--exiting\n");
    return 0;
}

别人的一些思路:个人觉得讲的一半清楚。
http://blog.chinaunix.net/uid-27164517-id-3282242.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容