C++条件变量和互斥锁

#include <thread>
#include <mutex>
#include <condition_variable>
#include <list>
#include <string>
#include <iostream>
#include <chrono>

class Event {
public:
    enum Type : int {
        quit = 0 ,
        help = 1
    };

    explicit Event(Type type)
        : m_type(type)
    { }

    virtual ~Event()
    { }

    Type type() const { return m_type; }
private:
    Type m_type;
};

class QuitEvent
        : public Event
{
public:
    explicit QuitEvent(int exitCode = 0)
        : Event(quit)
        , m_exitCode(exitCode)
    { }

    void setExitCode(int exitCode) { m_exitCode = exitCode; }
    int exitCode() const { return m_exitCode; }

private:
    int m_exitCode;
};

class HelpEvent
    : public Event
{
public:
    explicit HelpEvent(const std::string& msg)
        : Event(help)
        , m_msg(msg)
    {}

    void setMsg(const std::string& msg) { m_msg = msg; }

    std::string msg() const { return m_msg; }

private:
    std::string m_msg;
};

class EventQueue {
public:
    Event* GetEvent()
    {
        std::unique_lock<std::mutex> locker(m_evtQueueMtx);
        while(m_eventQueue.empty())
            m_evtQueueCondVar.wait(locker);
        Event* evt = m_eventQueue.front();
        m_eventQueue.pop_front();
        return evt;
    }

    void PushEvent(Event* evt)
    {
        m_evtQueueMtx.lock();
        const bool bNeedNotify = m_eventQueue.empty();
        m_eventQueue.push_back(evt);
        m_evtQueueMtx.unlock();
        if (bNeedNotify)
            m_evtQueueCondVar.notify_all();
    }

private:
    std::mutex m_evtQueueMtx;
    std::condition_variable m_evtQueueCondVar;
    std::list<Event*> m_eventQueue;
};

void thread_proc(const std::string& name , EventQueue *queue)
{
    for(;;)
    {
        Event *evt = queue->GetEvent();
        if (evt->type() == Event::quit)
        {
            QuitEvent* e = static_cast<QuitEvent*>(evt);
            std::cout << "thread " << name << " quit. Quit code : " << e->exitCode() << std::endl;
            delete e;
            break;
        }
        else if (evt->type() == Event::help)
        {
            HelpEvent *e = static_cast<HelpEvent*>(evt);
            std::cout << "thread " << name << " get a help event. Msg : " << e->msg() << std::endl;
            delete e;
        }
        else
        {
            std::cout << "thread " << name << " get an event. Type : " << evt->type() << std::endl;
        }
    }
}

int main(int argc, char *argv[])
{
    EventQueue evtQueue;
    std::thread thread1(thread_proc , "thread1" , &evtQueue);
    std::thread thread2(thread_proc , "thread2" , &evtQueue);
    std::thread thread3(thread_proc , "thread3" , &evtQueue);
    std::thread thread4(thread_proc , "thread4" , &evtQueue);
    std::thread thread5(thread_proc , "thread5" , &evtQueue);
    std::thread thread6(thread_proc , "thread6" , &evtQueue);

    for(int i = 0; i < 1000; ++i)
    {
        if (rand() % 2 == 0)
            evtQueue.PushEvent(new Event(static_cast<Event::Type>(rand())));
        else
            evtQueue.PushEvent(new HelpEvent(std::to_string(rand() % 500) + "--help msg"));

        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }

    for(int i = 0; i < 6; ++i)
    {
        evtQueue.PushEvent(new QuitEvent(qrand() % 500));
    }

    thread1.join();
    thread2.join();
    thread3.join();
    thread4.join();
    thread5.join();
    thread6.join();

    std::cout << "All Quit!" << std::endl;

    return 0;
}

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

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

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

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

转载于:https://my.oschina.net/lCQ3FC3/blog/1593048

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值