精华内容
下载资源
问答
  • c++多线程条件变量

    千次阅读 2019-04-22 21:41:37
    1.条件变量condition_variable、wait()、notify_one()(通知一个线程) 1.1condition_variable实际上是一个类,是一个和条件相关的一个类,等待一个条件达成,这个类需要和互斥量来配合工作,用的时候我们要生成...

    1.条件变量condition_variable、wait()、notify_one()(通知一个线程)

    1.1condition_variable实际上是一个类,是一个和条件相关的一个类,等待一个条件达成,这个类需要和互斥量来配合工作,用的时候我们要生成这个类的对象。

    1.2.1wait()函数用于等待一个消息,他的第二个参数lambda表达式返回值是true,那wait()直接返回,否则wait()将解锁互斥量,并堵塞到本行,堵塞到其他某个线程调用notify_one()成员函数为止。如果wait()没有第二个参数,那么结果和lambda表达式返回false一样。

    1.2.2当线程调用notify_one()唤醒wait()(原先堵塞状态)时,wait()恢复后,不断地尝试重新获取互斥量锁,如果获取不到,那么流程卡在wait这里等着获取,如果获取到了锁,那么就判断wait()是都有第二个参数,如果有,那么判断lambda表达式值的真假,如果为true,则wait返回,流程走下去(此时互斥锁被锁着),如果为false那么wait对互斥量解锁,继续睡眠堵塞状态。如果没有结果和true的结果是一致的。

    注:notify_one的效果不一定能快速唤醒wait(),如果另外一个线程不是卡在wait处等待唤醒,在做其他事务是,唤醒是无效的。inMsgRecvQueue()与outMsgRecvQueue()调用的时候两个线程对锁的竞争,会出现inMsgRecvQueue()调用更多,导致插入的数据过多。通过lambda表达式判断取数据时是否有数据,可以避免虚假唤醒。

    lambda表达式的使用参考https://www.cnblogs.com/DswCnblog/p/5629165.html

      1 #include<iostream>                                                                
      2 #include<thread>                                                                  
      3 #include<list>                                                                    
      4 #include<mutex>                                                                   
      5 #include<condition_variable>                                                      
      6                                                                                   
      7 using namespace std;                                                              
      8                                                                                   
      9 class A{                                                                          
     10 public:                                                                           
     11 //把收到的消息入到一个队列的线程                                                  
     12 void inMsgRecvQueue(){                                                            
     13     for(int i = 0;i < 100000; ++i){                                               
     14         cout<<"inMsgRecvQueue(_)执行,插入一个元素"<<i<<endl;                      
     15         std::unique_lock<std::mutex> sbguard1(my_mutex1);                         
     16         msgRecvQueue.push_back(i);                                                
     17         my_cond.notify_one();  //通过调用该函数唤醒wait                           
     18     }                                                                             
     19     return;                                                                       
     20 }                                                                                 
     21                                                                                   
     22 //把数据从消息队列中取出的进程                                                    
     23   void outMsgRecvQueue(){                                                         
     24         int command = 0;                                                          
     25         while(true){                                                              
     26             std::unique_lock<std::mutex> sbguard1(my_mutex1);                     
     27             my_cond.wait(sbguard1,[this]{                                         
     28                 if(!msgRecvQueue.empty())                                         
     29                     return true;                                                  
     30                 return false;                                                     
     31         });                                                                       
     23   void outMsgRecvQueue(){                                                         
     24         int command = 0;                                                          
     25         while(true){                                                              
     26             std::unique_lock<std::mutex> sbguard1(my_mutex1);                     
     27             my_cond.wait(sbguard1,[this]{                                         
     28                 if(!msgRecvQueue.empty())                                         
     29                     return true;                                                  
     30                 return false;                                                     
     31         });                                                                       
     32         command = msgRecvQueue.front();                                           
     33         msgRecvQueue.pop_back();                                                  
     34         sbguard1.unlock();                                                                        
     35         cout<<"outMsgRecvQueue()执行,取出一个元素";                              
     36         }                                                                         
     37     }                                                                             
     38 private:                                                                          
     39     list<int> msgRecvQueue;                                                       
     40     mutex my_mutex1;                                                              
     41     std::condition_variable  my_cond; //生成一个条件变量对象                      
     42 };  
     43 int main(){                                                                       
     44     A myobja;                                                                     
     45     thread myOutMsgobj(&A::outMsgRecvQueue,&myobja);                              
     46     thread myInMsgobj(&A::inMsgRecvQueue,&myobja);                                
     47     myInMsgobj.join();                                                            
     48     myOutMsgobj.join();                                                           
     49     return 0;                                                                     
     50 }                              

    2.notify_all()(同时唤醒多个线程)

      1 #include<iostream>                                                                                
      2 #include<thread>                                                                  
      3 #include<list>                                                                    
      4 #include<mutex>                                                                   
      5 #include<condition_variable>                                                      
      6                                                                                   
      7 using namespace std;                                                              
      8                                                                                   
      9 class A{                                                                          
     10 public:                                                                           
     11 //把收到的消息入到一个队列的线程                                                  
     12 void inMsgRecvQueue(){                                                            
     13     for(int i = 0;i < 100000; ++i){                                               
     14         std::unique_lock<std::mutex> sbguard1(my_mutex1);                         
     15         cout<<"inMsgRecvQueue(_)执行,插入一个元素"<<i<<endl;                      
     16         msgRecvQueue.push_back(i);                                                
     17         my_cond.notify_all();  //通过调用该函数唤醒wait                           
     18     }                                                                             
     19     return;                                                                       
     20 }                        
    21                                                                                   
     22 //把数据从消息队列中取出的进程                                                    
     23   void outMsgRecvQueue(){                                                         
     24         int command = 0;                                                          
     25         while(true){                                                              
     26             std::unique_lock<std::mutex> sbguard1(my_mutex1);                     
     27             my_cond.wait(sbguard1,[this]{                                         
     28                 if(!msgRecvQueue.empty())                                         
     29                     return true;                                                  
     30                 return false;                                                     
     31         });                                                                       
     32         command = msgRecvQueue.front();                                           
     33         msgRecvQueue.pop_back();                                                  
     34         cout<<"outMsgRecvQueue()执行,取出一个元素"<<command<<"threadId = "<<std::this_thread::get    _id()<<endl;
     35         sbguard1.unlock();                                                        
     36         }                                                                         
     37     }                                                                             
     38 private:                                                                          
     39     list<int> msgRecvQueue;                                                                                                       
     40     mutex my_mutex1;                                                              
     41     std::condition_variable  my_cond; //生成一个条件变量对象                      
     42 };                                                                                
     43 int main(){                                                                       
     44     A myobja;                                                                     
     45     thread myOutMsgobj(&A::outMsgRecvQueue,&myobja);                              
     46     thread myOutMsgobj2(&A::outMsgRecvQueue,&myobja);                             
     47     thread myInMsgobj(&A::inMsgRecvQueue,&myobja);                                
     48     myInMsgobj.join();                                                            
     49     myOutMsgobj.join();                                                           
     50     myOutMsgobj2.join();                                                          
     51     return 0;                                                                     
     52 }                   

    展开全文
  • c++多线程全局变量可见性问题

    千次阅读 2020-08-16 21:57:21
    最近学习多线程编程的时候,测试加锁不加锁的时候,碰到了一个不能理解的现象,通过搜索,发现多线程编程的坑很深,需要考虑很多方面的情况,如编译器优化,内存模型,cpu架构。 现象:没有使用锁的情况下,两个...

    最近学习多线程编程的时候,测试加锁不加锁的时候,碰到了一个不能理解的现象,通过搜索,发现多线程编程的坑很深,需要考虑很多方面的情况,如编译器优化,内存模型,cpu架构。

    现象:没有使用锁的情况下,两个线程访问同一个全局变量,一个线程修改了该全局变量后,在另一个线程中始终无法发现修改的值。

    大致的代码描述:

    int a = 0;
    void * threadFun1(void *)
    {
    	a = 1;
    }
    
    void * threadFun2(void *)
    {
    	while (1)
    	{
    		if (a == 1)
    		{
    			//do something
    		}
    	}
    }	
    

    线程函数threadFun2始终没有完成do something的任务,原因是获取到a的值始终是0,并没有刷新为由线程函数threadFun1修改的值1。
    本现象的原因是编译器优化造成的,线程函数threadFun2中由于频繁的访问aa的值用的是本线程缓存的值,因此不能看到主存中a的新值。测试下面两种方法有效:

    1. 设置编译器不优化。cmake中加入set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -00")
    2. 使用volatile关键字,强制每次从内存读取或刷新变量的值。volatile int a = 0

    这种现象就是共享变量的可见性问题,上面的两种方法都不是解决问题的真正方法,真正的方法是加锁,保证每次只有一个线程能访问共享的变量,并且刷新和读取最新的内存值。

    参考连接:
    https://www.cnblogs.com/soaringEveryday/p/4418604.html
    https://blog.csdn.net/u012887385/article/details/57984439
    https://www.codenong.com/18599012/
    https://www.it-swarm.dev/zh/c++/什么时候使用volatile多线程?/970260499/
    https://changkun.de/modern-cpp/zh-cn/07-thread/index.html

    展开全文
  • C++多线程并发(三)---线程同步之条件变量

    千次阅读 多人点赞 2019-05-03 12:43:12
    在前一篇文章《C++多线程并发编程(二)—线程同步之互斥锁》中解释了线程同步的原理和实现,使用互斥锁解决数据竞争访问问题,算是线程同步的加锁原语,用于排他性的访问共享数据。我们在使用mutex时,一般都会期望...

    一、何为条件变量

    在前一篇文章《C++多线程并发(二)—线程同步之互斥锁》中解释了线程同步的原理和实现,使用互斥锁解决数据竞争访问问题,算是线程同步的加锁原语,用于排他性的访问共享数据。我们在使用mutex时,一般都会期望加锁不要阻塞,总是能立刻拿到锁,然后尽快访问数据,用完之后尽快解锁,这样才能不影响并发性和性能。

    如果需要等待某个条件的成立,我们就该使用条件变量(condition variable)了,那什么是条件变量呢,引用APUE中的一句话:

    Condition variables are another synchronization mechanism available to threads.
    These synchronization objects provide a place for threads to rendezvous. When used with mutexes, condition variables allow threads to wait in a race-free way for arbitrary conditions to occur.

    条件变量是线程的另外一种有效同步机制。这些同步对象为线程提供了交互的场所(一个线程给另外的一个或者多个线程发送消息),我们指定在条件变量这个地方发生,一个线程用于修改这个变量使其满足其它线程继续往下执行的条件,其它线程则等待接收条件已经发生改变的信号。当条件变量同互斥锁一起使用时,条件变量允许线程以一种无竞争的方式等待任意条件的发生。

    二、为何引入条件变量

    前一章介绍了多线程并发访问共享数据时遇到的数据竞争问题,我们通过互斥锁保护共享数据,保证多线程对共享数据的访问同步有序。但如果一个线程需要等待一个互斥锁的释放,该线程通常需要轮询该互斥锁是否已被释放,我们也很难找到适当的轮训周期,如果轮询周期太短则太浪费CPU资源,如果轮询周期太长则可能互斥锁已被释放而该线程还在睡眠导致发生延误。

    下面给出一个简单的程序示例:一个线程往队列中放入数据,一个线程从队列中提取数据,取数据前需要判断一下队列中确实有数据,由于这个队列是线程间共享的,所以,需要使用互斥锁进行保护,一个线程在往队列添加数据的时候,另一个线程不能取,反之亦然。程序实现代码如下:

    //cond_var1.cpp用互斥锁实现一个生产者消费者模型
    
    #include <iostream>
    #include <deque>
    #include <thread>
    #include <mutex>
    
    std::deque<int> q;						//双端队列标准容器全局变量
    std::mutex mu;							//互斥锁全局变量
    //生产者,往队列放入数据
    void function_1() {
        int count = 10;
        while (count > 0) {
            std::unique_lock<std::mutex> locker(mu);
            q.push_front(count);			//数据入队锁保护
            locker.unlock();
            std::this_thread::sleep_for(std::chrono::seconds(1));		//延时1秒
            count--;
        }
    }
    //消费者,从队列提取数据
    void function_2() {
        int data = 0;
        while ( data != 1) {
            std::unique_lock<std::mutex> locker(mu);
            if (!q.empty()) {			//判断队列是否为空
                data = q.back();
                q.pop_back();			//数据出队锁保护
                locker.unlock();
                std::cout << "t2 got a value from t1: " << data << std::endl;
            } else {
                locker.unlock();
            }
        }
    }
    
    int main() {
        std::thread t1(function_1);
        std::thread t2(function_2);
        t1.join();
        t2.join();
    
        getchar();
        return 0;
    }
    

    程序执行结果如下:
    cond_var1执行结果
    从代码中不难看出:在生产过程中,因每放入一个数据有1秒延时,所以这个生产的过程是很慢的;在消费过程中,存在着一个while循环,只有在接收到表示结束的数据的时候,才会停止,每次循环内部,都是先加锁,判断队列不空,然后就取出一个数,最后解锁。所以说,在1s内,做了很多无用功!这样的话,CPU占用率会很高,可能达到100%(单核)。如下图示:
    cond_var1资源消耗
    既然是由于消费者在while循环内因等待数据做了过多的无用功导致CPU占有率过高,我们可以考虑在消费者发现队列为空时,让消费者小睡一会儿,即增加一个小延时(比如500ms),相当于增大了轮询间隔周期,应该能降低CPU的占用率。按该方案修改后的消费者代码如下:

    //消费者,从队列提取数据
    void function_2() {
        int data = 0;
        while ( data != 1) {
            std::unique_lock<std::mutex> locker(mu);
            if (!q.empty()) {			//判断队列是否为空
                data = q.back();
                q.pop_back();			//数据出队锁保护
                locker.unlock();
                std::cout << "t2 got a value from t1: " << data << std::endl;
            } else {
                locker.unlock();
                std::this_thread::sleep_for(std::chrono::milliseconds(500));		//延时500毫秒
            }
        }
    }
    

    增大轮询周期后,CPU占有率下降很明显:
    cond_var1改进后的资源消耗
    但前面也说了,困难之处在于如何确定这个延长时间(即轮询间隔周期),如果间隔太短会过多占用CPU资源,如果间隔太长会因无法及时响应造成延误。

    这就引入了条件变量来解决该问题:条件变量使用“通知—唤醒”模型,生产者生产出一个数据后通知消费者使用,消费者在未接到通知前处于休眠状态节约CPU资源;当消费者收到通知后,赶紧从休眠状态被唤醒来处理数据,使用了事件驱动模型,在保证不误事儿的情况下尽可能减少无用功降低对资源的消耗。

    三、如何使用条件变量

    C++标准库在< condition_variable >中提供了条件变量,借由它,一个线程可以唤醒一个或多个其他等待中的线程。原则上,条件变量的运作如下:

    • 你必须同时包含< mutex >和< condition_variable >,并声明一个mutex和一个condition_variable变量;
    • 那个通知“条件已满足”的线程(或多个线程之一)必须调用notify_one()或notify_all(),以便条件满足时唤醒处于等待中的一个条件变量;
    • 那个等待"条件被满足"的线程必须调用wait(),可以让线程在条件未被满足时陷入休眠状态,当接收到通知时被唤醒去处理相应的任务;

    将上面的cond_var1.cpp程序使用条件变量解决轮询间隔难题的示例代码如下:

    //cond_var2.cpp用条件变量解决轮询间隔难题
    
    #include <iostream>
    #include <deque>
    #include <thread>
    #include <mutex>
    #include <condition_variable>
    
    std::deque<int> q;						//双端队列标准容器全局变量
    std::mutex mu;							//互斥锁全局变量
    std::condition_variable cond;           //全局条件变量
    //生产者,往队列放入数据
    void function_1() {
        int count = 10;
        while (count > 0) {
            std::unique_lock<std::mutex> locker(mu);
            q.push_front(count);			//数据入队锁保护
            locker.unlock();
            
            cond.notify_one();              // 向一个等待线程发出“条件已满足”的通知
            
            std::this_thread::sleep_for(std::chrono::seconds(1));		//延时1秒
            count--;
        }
    }
    //消费者,从队列提取数据
    void function_2() {
        int data = 0;
        while ( data != 1) {
            std::unique_lock<std::mutex> locker(mu);
            
            while(q.empty())        //判断队列是否为空
                cond.wait(locker); // 解锁互斥量并陷入休眠以等待通知被唤醒,被唤醒后加锁以保护共享数据
    
            data = q.back();
            q.pop_back();			//数据出队锁保护
            locker.unlock();
            std::cout << "t2 got a value from t1: " << data << std::endl;
        }
    }
    
    int main() {
        std::thread t1(function_1);
        std::thread t2(function_2);
        t1.join();
        t2.join();
    
        getchar();
        return 0;
    }
    

    使用条件变量对CPU的占用率也很低,而且免去了轮询间隔该设多长的难题:
    cond_var2资源消耗
    上面的代码有三个注意事项:

    1. 在function_2中,在判断队列是否为空的时候,使用的是while(q.empty()),而不是if(q.empty()),这是因为wait()从阻塞到返回,不一定就是由于notify_one()函数造成的,还有可能由于系统的不确定原因唤醒(可能和条件变量的实现机制有关),这个的时机和频率都是不确定的,被称作伪唤醒。如果在错误的时候被唤醒了,执行后面的语句就会错误,所以需要再次判断队列是否为空,如果还是为空,就继续wait()阻塞;
    2. 在管理互斥锁的时候,使用的是std::unique_lock而不是std::lock_guard,而且事实上也不能使用std::lock_guard。这需要先解释下wait()函数所做的事情,可以看到,在wait()函数之前,使用互斥锁保护了,如果wait的时候什么都没做,岂不是一直持有互斥锁?那生产者也会一直卡住,不能够将数据放入队列中了。所以,wait()函数会先调用互斥锁的unlock()函数,然后再将自己睡眠,在被唤醒后,又会继续持有锁,保护后面的队列操作。lock_guard没有lock和unlock接口,而unique_lock提供了,这就是必须使用unique_lock的原因;
    3. 使用细粒度锁,尽量减小锁的范围,在notify_one()的时候,不需要处于互斥锁的保护范围内,所以在唤醒条件变量之前可以将锁unlock()。

    还可以将cond.wait(locker)换一种写法,wait()的第二个参数可以传入一个函数表示检查条件,这里使用lambda函数最为简单,如果这个函数返回的是true,wait()函数不会阻塞会直接返回,如果这个函数返回的是false,wait()函数就会阻塞着等待唤醒,如果被伪唤醒,会继续判断函数返回值。代码示例如下:

    //消费者,从队列提取数据
    void function_2() {
        int data = 0;
        while ( data != 1) {
            std::unique_lock<std::mutex> locker(mu);
            
            cond.wait(locker, [](){ return !q.empty();});   //如果条件变量被唤醒,检查队列非空条件是否为真,为真则直接返回,为假则继续等待
            
            data = q.back();
            q.pop_back();			//数据出队锁保护
            locker.unlock();
            std::cout << "t2 got a value from t1: " << data << std::endl;
        }
    }
    

    下面给出条件变量支持的操作函数表:
    条件变量操作函数
    值得注意的是:

    • 所有通知(notification)都会被自动同步化,所以并发调用notify_one()和notify_all()不会带来麻烦;
    • 所有等待某个条件变量(condition variable)的线程都必须使用相同的mutex,当wait()家族的某个成员被调用时该mutex必须被unique_lock锁定,否则会发生不明确的行为;
    • wait()函数会执行“解锁互斥量–>陷入休眠等待–>被通知唤醒–>再次锁定互斥量–>检查条件判断式是否为真”几个步骤,这意味着传给wait函数的判断式总是在锁定情况下被调用的,可以安全的处理受互斥量保护的对象;但在"解锁互斥量–>陷入休眠等待"过程之间产生的通知(notification)会被遗失。

    线程同步保证了多个线程对共享数据的有序访问,目前我们了解到的多线程间传递数据主要是通过共享数据(全局变量)实现的,全局共享变量的使用容易增加不同任务或线程间的耦合度,也增加了引入bug的风险,所以全局共享变量应尽可能少用。很多时候我们只需要传递某个线程或任务的执行结果,以便参与后续的运算,但我们又不想阻塞等待该线程或任务执行完毕,而是继续执行暂时不需要该线程或任务执行结果参与的运算,当需要该线程执行结果时直接获得,才能更充分发挥多线程并发的效率优势。想了解该问题,请继续阅读下一篇文章:《C++多线程并发(四)—异步编程》

    更多文章:

    展开全文
  • C++11多线程条件变量

    万次阅读 2016-06-04 22:12:07
    原文: ...std::condition_variable 定义在头文件&lt;condition_variable&gt; class condition_variable; (since C++11) condition_variable类是一个同步原语,可以被用来阻...

    原文: http://en.cppreference.com/w/cpp/thread/condition_variable

    std::condition_variable

    定义在头文件<condition_variable>
    class condition_variable;   (since C++11)

     

    condition_variable类是一个同步原语,可以被用来阻塞一个线程或者同时阻塞多个线程,直到另一个线程既修改了共享变量(即“条件”),也做了通知。


    想要修改共享变量(即“条件”)的线程必须:
    1. 获得一个std::mutex(一般来说是通过
    std::lock_guard获得)
    2. 当持有锁的时候,执行修改动作
    3. 对std::condition_variable执行notify_one或notify_all(当做notify动作时,不必持有锁)


    即使共享变量是原子性的,它也必须在mutex的保护下被修改,这是为了能够将改动正确发布到正在等待的线程。

    任意要等待std::condition_variable的线程必须:
    1. 获取
    std::unique_lock<std::mutex>,这个mutex正是用来保护共享变量(即“条件”)的
    2. 执行wait, wait_for或者wait_until. 这些等待动作原子性地释放mutex,并使得线程的执行暂停
    3. 当获得条件变量的通知,或者超时,或者一个虚假的唤醒,那么线程就会被唤醒,并且获得mutex. 然后线程应该检查条件是否成立,如果是虚假唤醒,就继续等待。

     

    【译注: 所谓虚假唤醒,就是因为某种未知的罕见的原因,线程被从等待状态唤醒了,但其实共享变量(即条件)并未变为true。因此此时应继续等待】

     

    std::condition_variable只能和std::unique_lock<std::mutex>一起工作;这个限制使得在某些平台上能够获得最大效率。std::condition_variable_any提供了一种能够和BasicLockable的对象一起工作的条件变量。

    【译注:BasicLockable指的是为实现互斥而所需的最小特性集,即符合BasicLockable的类型,其对象必须至少要能有lock()和unlock()的能力。】

    【译注:std::condition_variable_any是对std::condition_variable类的泛化。std::condition_variable只能和std::unique_lock<std::mutex>一起工作,而std::condition_variable_any则可以和只要是满足BasicLockable的用户自定义的lock类一起工作。】

     

    条件变量允许同时调用wait, wait_for, wait_until, notify_one和notify_all这些成员函数。
    std::condtion_variable类是StandardLayoutType. 它不是CopyConstructible,MoveConstructible, CopyAssignable, MoveAssignable的。

    成员类型

    成员类型                    定义
    native_handle_type      implementation-defined (译注:由实现定义)


    成员函数

    构造函数                构造对象(public)
    析构函数                析构对象(public)
    operator=[deleted]      not copy-assignable(public)
    notify_one              通知一个等待线程(public)
    notify_all              通知所有等待线程(public)
    wait                    阻塞当前线程直至条件变量被唤醒(public)
    wait_for                阻塞当前线程直至条件变量被唤醒或超时(public)
    wait_until              阻塞当前线程直至条件变量被唤醒或直到指定的时间点(public)
    native_handle           返回native handle(public)


    示例程序
    条件变量和std::mutex合用,这是为了线程间通信。

    #include <iostream>
    #include <string>
    #include <thread>
    #include <mutex>
    #include <condition_variable>
     
    std::mutex m;
    std::condition_variable cv;
    std::string data;
    bool ready = false;
    bool processed = false;
     
    void worker_thread()
    {
        // Wait until main() sends data
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return ready;});
     
        // after the wait, we own the lock.
        std::cout << "Worker thread is processing data\n";
        data += " after processing";
     
        // Send data back to main()
        processed = true;
        std::cout << "Worker thread signals data processing completed\n";
     
        // Manual unlocking is done before notifying, to avoid waking up
        // the waiting thread only to block again (see notify_one for details)
        lk.unlock();
        cv.notify_one();
    }
     
    int main()
    {
        std::thread worker(worker_thread);
     
        data = "Example data";
        // send data to the worker thread
        {
            std::lock_guard<std::mutex> lk(m);
            ready = true;
            std::cout << "main() signals data ready for processing\n";
        }
        cv.notify_one();
     
        // wait for the worker
        {
            std::unique_lock<std::mutex> lk(m);
            cv.wait(lk, []{return processed;});
        }
        std::cout << "Back in main(), data = " << data << '\n';
     
        worker.join();
    }

    输出

    main() signals data ready for processing
    Worker thread is processing data
    Worker thread signals data processing completed
    Back in main(), data = Example data after processing

    上面这段代码的work_thread函数也可以改写成下面这种形式:

    /*
        关键点是:
        1. wait()函数的内部实现是:先释放了互斥量的锁,然后阻塞以等待条件为真;
        2. notify系列函数需在unlock之后再被调用。
        3. 套路是:
            a. A线程拿住锁,然后wait,此时已经释放锁,只是阻塞了在等待条件为真;
            b. B线程拿住锁,做一些业务处理,然后令条件为真,释放锁,再调用notify函数;
            c. A线程被唤醒,接着运行。
    */
    
    #include <iostream>
    #include <string>
    #include <thread>
    #include <mutex>
    #include <condition_variable>
     
    std::mutex m;
    std::condition_variable cv;
    std::string data;
    bool ready = false;
    bool processed = false;
     
    void worker_thread()
    {
        {
            // Wait until main() sends data
            std::unique_lock<std::mutex> lk(m);
            cv.wait(lk, []{return ready;});
        }
     
        {
            std::unique_lock<std::mutex> lk(m);
            // after the wait, we own the lock.
            std::cout << "Worker thread is processing data\n";
            data += " after processing";
         
            // Send data back to main()
            processed = true;
            std::cout << "Worker thread signals data processing completed\n";
        }
        
        cv.notify_one();
    }
     
    int main()
    {
        std::thread worker(worker_thread);
     
        data = "Example data";
        // send data to the worker thread
        {
            std::unique_lock<std::mutex> lk(m);
            ready = true;
            std::cout << "main() signals data ready for processing\n";
        }
        cv.notify_one();
     
        // wait for the worker
        {
            std::unique_lock<std::mutex> lk(m);
            cv.wait(lk, []{return processed;});
        }
        
        std::cout << "Back in main(), data = " << data << '\n'; 
        worker.join();
    }
    

    最后,总结一下条件变量的几个关键点:

    1. wait()函数的内部实现是:先释放了互斥量的锁,然后阻塞以等待条件为真;
    2. notify系列函数需在unlock之后再被调用;
    3. 套路是:
            a. A线程拿住锁,然后wait,此时已经释放锁,只是阻塞了在等待条件为真;
            b. B线程拿住锁,做一些业务处理,然后令条件为真,释放锁,再调用notify函数;
            c. A线程被唤醒,接着运行。

     

    (完)

    展开全文
  • 最近在学习多线程的网络编程,互斥量和条件变量多线程编程中常用的线程同步方式。在编写自己的高并发服务器的过程中对互斥量和条件变量进行了封装,想要测试一下自己封装的类是否正确,能否通过自己封装的条件变量...
  •  互斥机制,包括互斥量【C/C++多线程编程之六】pthread互斥量,信号量【C/C++多线程编程之七】pthread信号量,互斥能很好的处理共享资源访问的协调问题,是多线程同步必不可少的机制。互斥机制也有其缺陷,当线程在...
  • C++11 多线程同步 互斥锁 条件变量

    千次阅读 2017-03-04 21:21:26
    多线程程序中,线程同步(多个线程访问一个资源保证顺序)是一个非常重要的问题,Linux下常见的线程同步的方法有下面几种: 互斥锁 条件变量 信号量 这篇博客只介绍互斥量和条件变量的使用。互斥锁和条件变量通常...
  • 主要知识——多线程,条件变量,锁。   这里其实有一个难点,当然可以在看了代码之后再回头来看,就是对std::condition_variable::wait方法的理解。 这里解释一下,当执行wait方法时,锁是会被解开的以便别的线程...
  • 多线程编程中,当多个线程之间需要进行某些同步机制时,如某个线程的执行需要另一个线程完成后才能进行,可以使用条件变量c++11提供的 condition_variable 类是一个同步原语,它能够阻塞一个或者多个线程,直到...
  • 当无法确定自己需要开多少线程来运行程序时可以用以下命令查看用于确认自己当前机器开多少线程效率是最高的(仅仅作为参考): 直接用lambda函数方式进行线程调用: ...在多线程分区间进行vector计算时会出问...
  • 它在linux C++中表示为sem_t包含在&lt;semaphore.h&gt;中。操作: 函数sem_init原型如下:/* Initialize semaphore object SEM to VALUE. If PSHARED then share it with other processes. */ extern in.....
  • c++11中,条件变量需要头文件: #include 同时,条件变量还需要一个mutex锁 条件变量实际上是如何运作的 ·线程1调用等待条件变量,内部获取mutex互斥锁并检查是否满足条件; ·如果没有,则释放锁,并等待条件...
  • c++多线程操作全局变量的锁的问题

    千次阅读 2019-11-11 11:45:12
    //join函数是main线程要等待tidA和tidB的线程结束后再退出 pthread_join(tidB,NULL); exit(0); } void * doit(void *vptr){ int i,val; for(i=0;i;i++){ pthread_mutex_lock(&counter_mutex);//这里需要...
  • 可以用原子操作变量来让变量能够在线程中使用 用atomic头文件引入原子操作 以下用atomic包裹变量让int变成原子操作 可以用aotmic操作变量进行如下操作从而保证变量的原子性: 但是当用两个变量两个原子...
  • C++11条件变量使用详解

    万次阅读 多人点赞 2019-05-02 00:31:21
    C++11中,我们可以使用条件变量(condition_variable)实现线程间的同步操作;当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线程才会被唤醒。 其主要成员函数如下: 条件变量是利用线程间共享...
  • 1相关理念 (1)类名 条件变量和互斥变量都是boost库中被封装的类。   ...互斥量是一种用于多线程编程的手段,它可以在多线程编程中防止多个线程同时操作共享资源[或称为临界区 ]。思想为:在每个线程
  • C++多线程模型是基于内存的,或者说是基于代码片段的,这和我们操作系统学习的临界区概念基本一致,但是与Golang不同,Golang是基于消息模型的。 一个std::mutex的lock()和unlock()之间的代码片段组成一个临界区,...
  • c++ 线程局部变量thread_local

    千次阅读 2020-01-14 10:07:52
     在Linux系统中使用C/C++进行多线程编程时,我们遇到最多的就是对同一变量多线程读写问题,大多情况下遇到这类问题都是通过锁机制来处理,但这对程序的性能带来了很大的影响,当然对于那些系统原生支持原子操作的...
  • C++ 多线程之创建多线程CreateThread

    千次阅读 2016-08-25 17:18:10
    一、为什么要写这篇博客一直对C++多线程一知半解,感觉没有实际进入过C++多线程的世界,因此想从头开始慢慢真正进入C++多线程,真正了解多线程。因为我也想了解Linux下的C++ 编程,因此我也会在Linux平台下进行编写...
  • 题目:编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C,每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推。 采用C++11实现: [cpp] view plaincopyprint? ...
  • 一般来说,多线程中如果需要等待一个变量或者条件为true 或者同步多个线程,有两种方法:1 . 忙等待,不停地检查该变量是否满足条件while(pre) // polling loop {}该方式有很多缺点:占用cpu资源,变量 pre 必须...
  • 目录 写在前面 解析 wait函数 wait_for函数 ...condition_variable条件变量可以阻塞(wait、wait_for、wait_until)调用的线程直到使用(notify_one或notify_all)通知恢复为止 首先要知道condition_v...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 170,309
精华内容 68,123
关键字:

c++多线程条件变量

c++ 订阅