zoukankan      html  css  js  c++  java
  • [development][C] 条件变量(condition variables)的应用场景是什么

    产生这个问题的起因是这样的:

    ‎[16:41] ‎<‎tong‎>‎ lilydjwg: 主线程要启动N个子线程, 一个局部变量作为把同样的参数传入每一个子线程. 子线程在开始的十行会处理完参数. 我发现这中间会有同步问题. 需要锁一下, 或者主线程要线性的逐个完成初始化.
    ‎[16:42] ‎<‎tong‎>‎ 主线程就是一个i<N的循环. 局部变量会根据i
    ‎[16:42] ‎<‎tong‎>‎ 修改
    ‎‎[16:53] <‎tong‎>‎ 就是, 线程A阻塞,  等待线程B处理完资源X后, 线程A将X的内容修改. 

    很显然, 这个问题, 只需要用一个信号量便可解决.

    但是有没有更好的方式呢?, 于是我想到了条件变量, 细研究了条件变量的用法之后发现, 条件变量需要一个条件变量同时还需要一个互斥锁来混合使用. 虽然可以实现我的需求, 但是很显然这很麻烦.

    所以, 问题在于, 这么麻烦的一组API, 它的应用场景是什么?

    经过查看资料, 分析, 讨论.之后. 大概的结论如下:

    ‎[17:55] ‎<‎tong‎>‎ 当条件复杂的时候, 就需要条件变量了. 写起来也会比较方便. 我的场景简单,就用信号量就行了. 大概是这么理解的

    一个比较详细的解释:

     https://www.zhihu.com/question/30641734

    作者:钓雪
    链接:https://www.zhihu.com/question/30641734/answer/105402533
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    需要condition变量的原因本质上就是程序执行顺序的不确定性.管程(monitor)只是保证了同一时刻只有一个进程在管程内活动,即管程内定义的操作在同一时刻只被一个进程调用(由编译器实现).但是这样并不能保证进程以设计的顺序执行,因此需要设置condition变量,让进入管程而无法继续执行的进程阻塞自己.也可以这么说,由于程执行顺序的不确定性,进程在entry序列中的顺序并不一定是我们想要的,而condition变量就是用来操作entry序列的.有了condition变量,我们就可以让在自己前驱进程之前提前进入管程的进程挂起自己,退回到entry序列中重新排队.具体来说: 以生产者-消费者问题(也称有限缓冲问题)为例, 如果我们不使用信号量(semaphore)而简单的用整形变量count来记录buffer中的数据项数目, 用系统调用sleep()阻塞进程 和 wakeup() 唤醒进程 程序如下#define TRUE 1
    #define MAX 100
    
    void insert_item(int);
    int  remove_item();
    
    int count = 0;
    
    void producer()
    {
        int item;
    
        while(TRUE)
        {
            item = produce_item(); 
            if( count == MAX ) sleep();
            insert_item(item);
            count += 1;
            if( count == 1) wakeup(consumer); /*buffer由空变非空后唤醒挂起的消费者*/
        }
    }
    
    void consumer()
    {
        int item;
    
        while(TRUE)
        {
            if( count == 0 ) sleep();
            item = remove_item();
            count -= 1;
            /*buffer由满变不满后唤醒生产者*/
            if( count == MAX - 1 ) wakeup(producer);
            consume_item(item);         
        }
    } 
    这样写的问题在于 我们不能保证 count 变量的互斥访问.如果producer在consumersleep之前发送wakeup信号便会导致错误,程序执行顺序如下1. 消费者 在检查到count == 0而未休眠2. 生产者 执行 count += 1 并且向 消费者发送 wakeup信号,3. 由于此时消费者并未sleep,wakeup信号丢失4. 之后消费者执行sleep休眠5. 生产者不断生产而消费者休眠直到buffer填满6. 生产者消费者都休眠,无法相互唤醒如果我们将访问count变量的过程写入管程中,就可以使两个进程对count进行互斥访问,从而解决这个问题.但是,管程并不知道编程者的逻辑,它并不能在生产者在count == MAX时挂起自己,也不能在 count == MAX - 1 时唤醒生产者,这时就需要使用条件变量.我们定义管程#define MAX 100
    
    /* 定义管程 PC */
    monitor PC
    {
        int count = 0; 
        /* 我们使用条件变量full 表示被填满的buffer, empty 表示空的buffer */
        conditon full, empty;
    
        void insert(int item)
        {
            /* 当buffer满的时候,我们在full上将插入操作阻塞 */
            if ( count == MAX ) wait(&full);
            insert_item(item);
            count += 1;
            /* 当buffer不空的时候,我们在empty上唤醒取出操作 */
            if ( count == MAX -1 ) signal(&empty);    
        }
    
        int remove()
        {
            /* 当buffer空的时候,我们在empty上将取出操作阻塞 */
            if( count == 0 ) wait(&empty);
            remove_item(item);
            count -= 1;
            /* 当buffer不满的时候,我们在full上唤醒插入操作 */
            return item;
            if( count == MAX - 1) signal(&full);
        }
    }
    
        void producer()
        {
            int item;
            item = produce_item();
            /*调用管程中的函数 */
            PC.insert(item);
        }
        
        void consumer()
        {
            int item;
            /*调用管程中的函数 */
            item = PC.remove();
            consumer_item();
        }
    *** 注意 以上代码 用类似于C的代码写成 ,但 C语言并不支持管程 ****** JAVA 支持管程 在method前加sychronized 关键字 可以保证在一个线程调用   ****** synchronized method时 不允许其他线程调用该对象中其他synchronized method  ***这样以来, 由于管程保证内部内部操作的互斥性,操作就不会wait()的前一刻被signal(),从而不会发生生产者和消费者都被阻塞而无法相互唤醒的情况了.同时我们的生产者和消费者也可以在恰当的时候阻塞自己.条件变量不止存在于管程中,在多线程库中也是普遍存在的.Unix的多线程库Pthread中也使用条件变量协调进程的同步.
    View Code

    另外, 原来, 这个东西叫管程 https://zh.wikipedia.org/wiki/%E7%9B%A3%E8%A6%96%E5%99%A8_(%E7%A8%8B%E5%BA%8F%E5%90%8C%E6%AD%A5%E5%8C%96)#.E6.A2.9D.E4.BB.B6.E8.AE.8A.E6.95.B8.28Condition_Variable.29

    值得一提的是: 东尼·霍尔证明了管程在逻辑上与信号量是等价的。

    此外,还发现了一个以前没有接触过的概念和API pthread_barrier_wait

    http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_barrier_wait.html

    最后,顺便一贴,没啥关系的

    https://jin-yang.github.io/post/program-c-linux-pthreads-synchronize.html

    ------------- 新的想法 @ 2017-11-09 ----------------

    基于昨天讨论的条件变量和信号量等价问题. 我发现原来互斥锁也是信号量实现的. 那是不是说,在线程或进程同步的时候,光有信号量就够了?

  • 相关阅读:
    03_ if 练习 _ little2big
    uva 11275 3D Triangles
    uva 12296 Pieces and Discs
    uvalive 3218 Find the Border
    uvalive 2797 Monster Trap
    uvalive 4992 Jungle Outpost
    uva 2218 Triathlon
    uvalive 3890 Most Distant Point from the Sea
    uvalive 4728 Squares
    uva 10256 The Great Divide
  • 原文地址:https://www.cnblogs.com/hugetong/p/7805583.html
Copyright © 2011-2022 走看看