C++多线程条件变量

C++多线程中的条件变量的使用。

在多线程编程中,常常使用条件变量来等待某个事件的发生。

先看代码

  1 #include <thread>
  2 #include <mutex>
  3 #include <condition_variable>
  4 #include <list>
  5 #include <string>
  6 #include <iostream>
  7 #include <chrono>
  8
  9 class Event {
 10 public:
 11     enum Type : int {
 12         quit = 0 ,
 13         help = 1
 14     };
 15
 16     explicit Event(Type type)
 17         : m_type(type)
 18     { }
 19
 20     virtual ~Event()
 21     { }
 22
 23     Type type() const { return m_type; }
 24 private:
 25     Type m_type;
 26 };
 27
 28 class QuitEvent
 29         : public Event
 30 {
 31 public:
 32     explicit QuitEvent(int exitCode = 0)
 33         : Event(quit)
 34         , m_exitCode(exitCode)
 35     { }
 36
 37     void setExitCode(int exitCode) { m_exitCode = exitCode; }
 38     int exitCode() const { return m_exitCode; }
 39
 40 private:
 41     int m_exitCode;
 42 };
 43
 44 class HelpEvent
 45     : public Event
 46 {
 47 public:
 48     explicit HelpEvent(const std::string& msg)
 49         : Event(help)
 50         , m_msg(msg)
 51     {}
 52
 53     void setMsg(const std::string& msg) { m_msg = msg; }
 54
 55     std::string msg() const { return m_msg; }
 56
 57 private:
 58     std::string m_msg;
 59 };
 60
 61 class EventQueue {
 62 public:
 63     Event* GetEvent()
 64     {
 65         std::unique_lock<std::mutex> locker(m_evtQueueMtx);
 66         while(m_eventQueue.empty())
 67             m_evtQueueCondVar.wait(locker);
 68         Event* evt = m_eventQueue.front();
 69         m_eventQueue.pop_front();
 70         return evt;
 71     }
 72
 73     void PushEvent(Event* evt)
 74     {
 75         m_evtQueueMtx.lock();
 76         const bool bNeedNotify = m_eventQueue.empty();
 77         m_eventQueue.push_back(evt);
 78         m_evtQueueMtx.unlock();
 79         if (bNeedNotify)
 80             m_evtQueueCondVar.notify_all();
 81     }
 82
 83 private:
 84     std::mutex m_evtQueueMtx;
 85     std::condition_variable m_evtQueueCondVar;
 86     std::list<Event*> m_eventQueue;
 87 };
 88
 89 void thread_proc(const std::string& name , EventQueue *queue)
 90 {
 91     for(;;)
 92     {
 93         Event *evt = queue->GetEvent();
 94         if (evt->type() == Event::quit)
 95         {
 96             QuitEvent* e = static_cast<QuitEvent*>(evt);
 97             std::cout << "thread " << name << " quit. Quit code : " << e->exitCode() << std::endl;
 98             delete e;
 99             break;
100         }
101         else if (evt->type() == Event::help)
102         {
103             HelpEvent *e = static_cast<HelpEvent*>(evt);
104             std::cout << "thread " << name << " get a help event. Msg : " << e->msg() << std::endl;
105             delete e;
106         }
107         else
108         {
109             std::cout << "thread " << name << " get an event. Type : " << evt->type() << std::endl;
110         }
111     }
112 }
113
114 int main(int argc, char *argv[])
115 {
116     EventQueue evtQueue;
117     std::thread thread1(thread_proc , "thread1" , &evtQueue);
118     std::thread thread2(thread_proc , "thread2" , &evtQueue);
119     std::thread thread3(thread_proc , "thread3" , &evtQueue);
120     std::thread thread4(thread_proc , "thread4" , &evtQueue);
121     std::thread thread5(thread_proc , "thread5" , &evtQueue);
122     std::thread thread6(thread_proc , "thread6" , &evtQueue);
123
124     for(int i = 0; i < 1000; ++i)
125     {
126         if (rand() % 2 == 0)
127             evtQueue.PushEvent(new Event(static_cast<Event::Type>(rand())));
128         else
129             evtQueue.PushEvent(new HelpEvent(std::to_string(rand() % 500) + "--help msg"));
130
131         std::this_thread::sleep_for(std::chrono::milliseconds(10));
132     }
133
134     for(int i = 0; i < 6; ++i)
135     {
136         evtQueue.PushEvent(new QuitEvent(qrand() % 500));
137     }
138
139     thread1.join();
140     thread2.join();
141     thread3.join();
142     thread4.join();
143     thread5.join();
144     thread6.join();
145
146     std::cout << "All Quit!" << std::endl;
147
148     return 0;
149 }

上述代码中,有几个问题需要澄清:

1.为什么66、67行代码有一个while循环。

2.为什么条件变量的使用必须带有一个互斥锁。

3.为什么条件变量使用的互斥锁和PushEvent函数使用的互斥锁是同一个。

4.互斥锁到底保护了什么.

问题1:

  为了更加有效的使用条件变量,我们使用了condition_variable::notify_all 来切换条件变量的状态。这样所有等待的线程都有机会被唤醒。在上述例子中假如thread1先被唤醒,之后thread2被唤醒,对于thread2来说,应当再一次检查事件列队中是否有可用事件,因为thread1或者别的先于thread2被唤醒的线程可能已经将事件列队清空。所以每一次线程被唤醒都应当再次检查事件列队是有事件可用。如果没有事件则应该再次进入等待状态。

问题2:

  条件变量能够在唤醒的同时加锁。唤醒和加锁是一个原子操作,这样当线程被唤醒是就能够立即获得资源的额访问权。当访问共享资源时应当在访问前加锁,如果不满足访问条件则应该释放锁并且进入等待状态,这样别的线程才能够访问共享资源。如果条件变量不带互斥锁,则当条件变量被唤醒时,应当对共享资源加锁。则应当写一下的伪代码:

forever {

  lock

  if (ok)

  {

    access;

    unlock;

    break;

  }

  else

  {

    unlock;

    wait;

  }

}

  从上述代码看出有更多的加锁和解锁操作。当线程进入等待时会进入内核状态,多次的加锁和解锁等待会造成线程在用户态和内核态之前频繁切换,这会带来性能问题,也容易使得编写有bug的代码。

问题3:

  从对问题2的分析可以看出,两个地方使用的互斥锁是为了保护同一个资源。为了保持访问的唯一性,因此必须是同一个互斥锁。

问题4:

  到此,问题4就很简单了,互斥锁保护的是被等待的资源。上述例子中是事件列队。

by linannk

2016.06.03 01:02

时间: 2024-11-25 12:50:29

C++多线程条件变量的相关文章

(转载)Linux 多线程条件变量同步

条件变量是线程同步的另一种方式,实际上,条件变量是信号量的底层实现,这也就意味着,使用条件变量可以拥有更大的自由度,同时也就需要更加小心的进行同步操作.条件变量使用的条件本身是需要使用互斥量进行保护的,线程在改变条件状态之前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为互斥量必须在锁定之后才能计算条件. 模型 #include<pthread.h> pthread_cond_t cond //准备条件变量 pthread_cond_t cond = PTHREAD_CON

C++多线程の条件变量

如果有一个队列,方法一和方法二:方法一是生产者,方法二是消费者: 两者不停的相互等待,加减锁,为了减少不必要的等待,我们可以使用条件变量, 条件的变量的第二个参数的加入可以控制多个线程的"消费"情况

笔记3 linux 多线程 条件变量+互斥锁

//cond lock #include<stdio.h> #include<unistd.h> #include<pthread.h> struct test { char a[10]; char b[10]; char c[10]; }yb = {"111","222","33333"}; static int j=0; pthread_mutex_t mutex_1 = PTHREAD_MUTEX_INI

Linux多线程编程的条件变量

在stackoverflow上看到一关于多线程条件变量的问题,题主问道:什么时候会用到条件变量,mutex还不够吗?有个叫slowjelj的人做了很好的回答,我再看这个哥们其他话题的一些回答,感觉水平好高的.这里把他的回答粘贴一下方便以后查看,原帖在这里:When is a conditional variable needed, isn't a mutex enough? Even though you can use them in the way you describe, mutexes

多线程同步条件变量(转载)

最近看<UNIX环境高级编程>多线程同步,看到他举例说条件变量pthread_cond_t怎么用,愣是没有看懂,只好在网上找了份代码,跑了跑,才弄明白 [cpp] view plaincopy #include <pthread.h> #include <stdio.h> #include <stdlib.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/*初始化互斥锁*/ pthread_cond_

linux C++ 多线程使用pthread_cond 条件变量

1. 背景 多线程中经常需要使用到锁(pthread_mutex_t)来完成多个线程之间的互斥操作. 但是互斥锁有一个明显到缺点: 只有两种状态,锁定和非锁定. 而条件变量则通过允许线程阻塞并等待另一个线程发送唤醒信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用. 2. 条件变量涉及到的主要函数 2.1 pthread_cond_wait 线程阻塞在条件变量 int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex); 函

linux多线程-互斥&amp;条件变量与同步

多线程代码问题描述 我们都知道,进程是操作系统对运行程序资源分配的基本单位,而线程是程序逻辑,调用的基本单位.在多线程的程序中,多个线程共享临界区资源,那么就会有问题: 比如 #include <pthread.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> int g_val = 10; void * test1(void* args) { g_val = 20; printf(&

Linux多线程编程-条件变量

条件变量 如果说线程间的互斥锁是用来同步共享数据的访问的话,那么条件变量是用于线程之间共享数据的值.条件变量提供了一种线程之间的通知机制,当某个共享数据达到某个值时,唤醒等待这个共享数据的线程.条件变量相关函数主要 有5个: #include <pthread.h> int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr); int pthread_cond_destroy(pthread_

转载~kxcfzyk:Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解

Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解 多线程c语言linuxsemaphore条件变量 (本文的读者定位是了解Pthread常用多线程API和Pthread互斥锁,但是对条件变量完全不知道或者不完全了解的人群.如果您对这些都没什么概念,可能需要先了解一些基础知识) 关于条件变量典型的实际应用,可以参考非常精简的Linux线程池实现(一)——使用互斥锁和条件变量,但如果对条件变量不熟悉最好先看完本文. Pthread库的条件变量机制的主要API有三个: int p