C++11新特性之线程操作

  C++11之前没有对并发编程提供语言级别的支持,这使得我们在编写可移植的并发程序时,存在诸多的不便。现在C++11增加了线程以及线程相关的类,很方便地支持了并发编程,使得编写的多线程程序的可移植性得到了很大的提高。

1. 线程

1.1 线程的创建

  #inclde <thread>

  std::thead t(ThreadFun, parm1, parm2,...);

  t.join();或t.detach();

  join会阻塞线程,直到线程函数结束

  detach让线程和线程对象分离,让线程作为后台线程去执行,但需要注意,detach之后就无法再和线程发生联系了,等于说失去了对线程的控制权。

2. 互斥量

  C++11提供了以下4中语义的互斥量:

  std::mutex:独占互斥量,不能递归使用

  std::timed_mutex:带超时的独占互斥量,不能递归使用

  std::recursive_mutex:递归互斥量,不带超时功能

  std::recursive_timed_mutex:带超时的递归互斥量

2.1 独占互斥量

  std::mutex m_mutex;

  mutex.lock();

  do something;

  mutex.unlock();

  注意:使用std::lock_guard<mutex> locker(m_mutex);可以简化lock/unlock的写法,同时也更安全,因为lock_guard在构造的时候会自动锁定互斥量,而在退出作用域后进行析构时就会自动解锁,从而保证了互斥量的正确操作。

  try_lock()尝试锁定互斥量,如果成功则返回true

2.2 递归的独占互斥量

  需要注意的是尽量不要使用递归锁:

  (1)需要用到递归锁的多线程互斥处理本身就应该可以简化的,运行递归互斥很容易放纵复杂逻辑的产生

  (2)递归锁比起非递归锁要麻烦,效率低

  (3)递归锁虽然允许同一个线程多次获得同一个互斥量,可重复获得的最大次数并未具体说明,一旦超过一定次数会抛出std::system错误

2.3 带超时的互斥量和递归带超时的互斥量

  std::timed_mutex比std::mutex多了两个超时获取锁的接口,try_lock_for和try_lock_until,这两个接口是用开设置获取互斥量的超时时间,使用时可以用一个while循环去不断地获取互斥量。

  std::timed_mutex mutex;

  std::chrono::secord timeout(2);

  if (mutex.try_lock_for(timeout))

    cout << "do work with the mutex" << endl;

  else:

    cout << "do work without the mutex" << endl;

3. 条件变量 

3.1 说明

  条件变量用于线程的同步机制,它能阻塞一个或多个线程,直到收到另外一个线程发出的同质或者超时,才会唤醒当前阻塞的线程。条件变量需要和互斥变量结合起来用。

  C++提供了两种条件变量:

  (1)condition_variable,配合std::unique_lock<std::mutex>进行wait操作

  (2)condition_variable_any,和任意带有lock,unlock语义的mutex搭配使用,比较灵活,但效率比condition_variable低

  注意以下函数的使用:

  (1)std::lock_guard,它利用了RAII机制可以保证mutex的安全释放

  (2)std::unique_lock与lock_guard的区别在与,前者可以自由地释放mutex,而后者需要等到std::lock_guard变量生命周期结束时才能释放。

3.2 示例实现消息循环队列

 3.2.1 实现代码

// 使用C++11的新特性实现线程安全的循环消息队列
#pragma once

#include<iostream>
#include<mutex>
#include<condition_variable>
using namespace std;

#define MAXQUEUELEN 32
template<typename T>
class CMsgQueue
{
public:
    CMsgQueue()
    {
        m_pQueue = new T[MAXQUEUELEN];
        m_nHead = m_nTail = 0;
    }
    ~CMsgQueue()
    {
        Clear();
    }

    void Push(T msg)
    {
        unique_lock<mutex> lock(m_Mutex);
        while (IsFull())
        {
            cout << "消息队列已经满,请等待..." << endl;
            m_ConditionVar.wait(lock);
        }
        cout << "Push: " << msg << endl;
        m_pQueue[m_nTail] = msg;
        m_nTail = m_nTail % MAXQUEUELEN + 1;
        m_ConditionVar.notify_all();
    }

    bool IsFull()
    {
        return (m_nTail + 1) % MAXQUEUELEN == m_nHead;
    }
    bool IsEmpty()
    {
        return m_nTail == m_nHead;
    }
    T Pop()
    {
        unique_lock<mutex> lock(m_Mutex);
        while (IsEmpty())
        {
            cout << "消息队列为空,请等待..." << endl;
            m_ConditionVar.wait(lock);
        }
        T msg = m_pQueue[m_nHead];
        cout << "Pop: " << msg << endl;
        m_nHead = m_nHead % MAXQUEUELEN + 1;
        m_ConditionVar.notify_all();
        return msg;
    }
    void Clear()
    {
        if (m_pQueue != NULL)
        {
            delete[] m_pQueue;
            m_pQueue = NULL;
        }
    }

private:
    T *m_pQueue;
    int m_nHead;
    int m_nTail;

    mutex m_Mutex;
    condition_variable m_ConditionVar;
};

3.2.2 测试

void fun1(CMsgQueue<int> *pQueue)
{
    for (int i = 0; i < 40; i++)
    {
        //this_thread::sleep_for(chrono::seconds(2));
        pQueue->Push(i);
    }
}

void fun2(CMsgQueue<int> *pQueue)
{
    for (int i = 0; i < 20; i++)
    {
        this_thread::sleep_for(chrono::seconds(2));
        pQueue->Pop();
    }
}

void main()
{
    CMsgQueue<int> *pQueue = new CMsgQueue<int>;
    thread t1(fun1, pQueue);
    thread t2(fun2, pQueue);

    t1.join();
    t2.join();

    return;
}

posted @ 2018-05-16 18:26  Fate0729  阅读(1414)  评论(0编辑  收藏  举报