多线程笔记

设计模式

大的项目分解成一个个小的模块。

单例设计模式(高频使用)

整个项目中,有某个或者某些特殊的类,属于类的对象,只能创建一个,多的创建不了。

// 单例模式示范代码
class MyCas
{
private:
    MyCas() {}  //私有化构造函数,避免该类创建多个
private:
    static MyCas* m_instance;
    static mutex m_mutex;
public:
    static MyCas* GetInstance()
    {
        if (m_instance == NULL)  //双重锁定,双重检查,保证只有当m_instance不为空的时候才加锁,提高效率
        {
            unique_lock<mutex> mymutex(m_mutex); 
            if (m_instance == NULL)
            {
                m_instance = new MyCas();
                static CCarhuishouMyCas cl;  //生命周期直到程序结束,以至于当这个对象释放时调用析构函数释放new出来的对象
            }
        }
         return m_instance;
    }
    class CCarhuishouMyCas  //类中套类来释放new出来的对象
    {
    public:
        ~CCarhuishouMyCas()
        {
            if (MyCas::m_instance)
            {
                delete MyCas::m_instance;
                MyCas::m_instance = NULL;
            }
        }
    };
};
MyCas* MyCas::m_instance = NULL;
mutex MyCas::m_mutex;
int main()
{
    MyCas* p_a = MyCas::GetInstance();
    return 0;
}

单例模式共享数据问题分析解决解决

若数据是只读不写的,就不需要加锁保护
面临的问题:需要多个线程中去创建单例类,这时有可能会出现GetInstance()这种成员函数要互斥
解决方法:就是要改进getinstance函数

//改进前
 static MyCas* GetInstance()
    {
        if (m_instance == NULL)
        {
            m_instance = new MyCas();
            static CCarhuishouMyCas cl;  //生命周期直到程序结束,以至于当这个对象释放时调用析构函数释放new出来的对象
        }
        return m_instance;
    }
   //改进后
    static MyCas* GetInstance()
    {
        if (m_instance == NULL)  //双重锁定,双重检查,保证只有当m_instance不为空的时候才加锁,提高效率
        {
            unique_lock<mutex> mymutex(m_mutex); 
            if (m_instance == NULL)
            {
                m_instance = new MyCas();
                static CCarhuishouMyCas cl;  //生命周期直到程序结束,以至于当这个对象释放时调用析构函数释放new出来的对象
            }
        }
        
        return m_instance;
    }

std::call_once()

c++11引入的函数,该函数的第二个参数是一个函数名a();
call_once()功能可以保证函数a()只被调用一次
call_once()具备互斥量的能力,并且效率上比互斥量消耗的资源少。
它主要是通过绑定一个std::one_flag结构(标志位实现的)。
下面示范通过callz_once()改造单例模式,替代其互斥量。

// 单例模式示范代码
class MyCas
{
private:
    MyCas() {}  //私有化构造函数,避免该类创建多个
private:
    static MyCas* m_instance;
    static once_flag g_flag;
public:
    static void CreateInstance()//这里表示只被调用一次的函数
    {
        m_instance = new MyCas();
        static CCarhuishouMyCas cl;  //生命周期直到程序结束,以至于当这个对象释放时调用析构函数释放new出来的对象
    }
    static MyCas* GetInstance()
    {
        call_once(g_flag, CreateInstance);
        return m_instance;
    }
    class CCarhuishouMyCas  //类中套类来释放new出来的对象
    {
    public:
        ~CCarhuishouMyCas()
        {
            if (MyCas::m_instance)
            {
                delete MyCas::m_instance;
                MyCas::m_instance = NULL;
            }
        }
    };
};
MyCas* MyCas::m_instance = NULL;
once_flag MyCas:: g_flag;
int main()
{
    MyCas* p_a = MyCas::GetInstance();
    return 0;
}

std::condition_variable ,wait(),notify_one,notify_all();

std::condition_variable
实际上是一个类,是需要和互斥量mutex配合使用,使用的时候要生成这个类
wait()
用来等待一个东西,当第二个参数返回值是false时候,wait()将解锁互斥量,并且堵塞到本行;堵塞到其他线程调用notify_one()成员函数为止如果没有第二个参数,那默认效果和第二个参数为false一样,立即堵塞。
当其他线程调用notify_one()将wait唤醒后,wait就解除阻塞状态。然后wait会不断尝试重新获取互斥量的锁,如果获取不到wait会卡在这里等待锁的获取,当获取到锁后,
如果wait第二个参数是lamda表达式,会继续执行lamda表达式,此时如果为true,继续执行(此时还是加锁状态
如果此时没有第二个参数,则默认是true;往下继续执行。一般来说wait()需要第二个参数,防止虚假唤醒
notify_all() 则是唤醒全部wait(),在下面示例代码中,其实notify_one 和notify_all效果是一样的。

#include <iostream>
#include<thread>
#include<mutex>
#include<list>
#include<Windows.h>
using namespace std;

class A {
public:
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 10000; i++)
        {
            cout << "inmsgRecvqueue执行,插入一个元素" << i<<endl;
            unique_lock<mutex> guard1(my_mutex);
            msgRecvQueue.push_back(i);
            my_cond.notify_one();//尝试把wait的线程唤醒,不一定能唤醒,如果线程此时没有卡在wait哪里的话
        }
    }
    void outMsgRecvQueue()
    {
        while(1)
        {
            unique_lock<mutex> guard1(my_mutex);
            my_cond.wait(guard1, [this] {
                if (!msgRecvQueue.empty())
                    return true;
                else
                    return false;
                });
                int k = msgRecvQueue.front();  
                cout << "取出" << k << endl;
                msgRecvQueue.pop_front();   
                guard1.unlock(); //因为用的是类模板此时还是锁着的,可以手动解锁,避免卡太长时间
                //......一些操作
        }
    }
public:
    list<int>msgRecvQueue;
    mutex my_mutex;
    condition_variable my_cond;
};
int main()
{
    A myojia;
    std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
    std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
    myOutmsgobj.join();
    myInmsgobj.join();
    return 0;
}

async future packaged_task promise

一 std::async
async是一个类模板,用于启动一个异步任务(即开启一个线程来执行对应的线程入口函数),然后会返回一个futrue对象(里面包括了该线程返回的结果,可以通过futrue对象的成员函数get()来获取结果) (future对象里面会保存一个值,在将来的某个时刻可以得到)

class A
{
public:
    int mythread(int k )
    {
        cout << "threadid" << std::this_thread::get_id() << endl;
        cout << k << endl;
        std::chrono::milliseconds dura(5000);
        std::this_thread::sleep_for(dura);
        return 5;
    }
};
int mythread()
{
    cout << "threadid" << std::this_thread::get_id() << endl;
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);
    return 5;
}
int main()
{
    int tm = 12;
    class A a;
    cout << "mainthreadid" << std::this_thread::get_id() << endl;
    future<int>result = std::async(&A::mythread,&a,tm);
    cout << ".........." << endl;
    cout << result.get() << endl;   //如果线程没执行完,线程会卡在这里等待线程执行完毕返回结果。
    cout << "!!!!!!" << endl;
    return 0;
}

此外,我们可以通过额外向async()传递一个参数,该参数类型是launch类(枚举类型)来达到一些效果
1,std::launch::deferred:表示线程如果函数调用延迟到std::future的wait()或者get()函数调用是才执行,且如果不调用wait()和get(),该线程直接不执行。即使调用wait()和get()也不创建新线程,都是主线程里面执行线程入口函数。

 future<int>result = std::async(std::launch::deferred,&A::mythread,&a,tm);

2,std::launch::async:表示在调用async函数的时候就开始创建线程

  future<int>result = std::async(std::launch::async,&A::mythread,&a,tm);

二 std::packaged_task
packaged_task是一个类模板,模板参数是各种可调用对象,通过packaged_task包装起来,方便将来作为线程入口函数来调用

 int mythread(int k)
{
    cout << "threadid" << std::this_thread::get_id() << endl;
    cout << k << endl;
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);
    return 5;
}
int main()
{
    cout << "mainthreadid" << std::this_thread::get_id() << endl;
    packaged_task<int(int)>mypt(mythread);
    thread t1(ref(mypt), 1);  //ref()指用引用的方式传递
    t1.join();
    std::future<int> result = mypt.get_future();
    cout << result.get() << endl;
    cout << "!!!!!" << endl;
    return 0;
}

//改为lamda表达式
int main()
{
    cout << "mainthreadid" << std::this_thread::get_id() << endl;
    packaged_task<int(int)>mypt([](int k) {
        
        cout << "threadid" << std::this_thread::get_id() << endl;
        cout << k << endl;
        std::chrono::milliseconds dura(5000);
        std::this_thread::sleep_for(dura);
        return 5;        
        });
    thread t1(ref(mypt), 1);  //ref()指用引用的方式传递
    t1.join();
    std::future<int> result = mypt.get_future();
    cout << result.get() << endl;
    cout << "!!!!!" << endl;
    return 0;
}
//也可以直接调用,但是就都在主线程中执行了。
int main()
{
    cout << "mainthreadid" << std::this_thread::get_id() << endl;
    packaged_task<int(int)>mypt([](int k) {
        
        cout << "threadid" << std::this_thread::get_id() << endl;
        cout << k << endl;
        std::chrono::milliseconds dura(5000);
        std::this_thread::sleep_for(dura);
        return 5;        
        });
    mypt(100);
    std::future<int> result = mypt.get_future();
   cout << result.get() << endl;
    cout << "!!!!!" << endl;
    return 0;
}

//和容器关联packaged_task
vector<packaged_task<int(int)>>mytasks;
int main()
{
    cout << "mainthreadid" << std::this_thread::get_id() << endl;
    packaged_task<int(int)>mypt([](int k) {
        
        cout << "threadid" << std::this_thread::get_id() << endl;
        cout << k << endl;
        std::chrono::milliseconds dura(5000);
        std::this_thread::sleep_for(dura);
        return 5;        
        });
    mytasks.push_back(std::move(mypt));//通过std::move,可以避免不必要的拷贝操作,是为性能而生,将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝。 
    packaged_task<int(int)>mypt2;
    auto it = mytasks.begin();
    mypt2 = std::move(*it);
    mytasks.erase(it);  //删除第一个元素,迭代器已经失效了,不能再使用it;
    mypt2(123);
    future<int>result = mypt2.get_future();
    cout << result.get() << endl;
    return 0;
}

二 std::promise
promise
可以能够在某个线程中给它赋值,然后我们可以在其他线程中,把这个值给取出来

void mythread(promise<int>& tmmp, int calc)
{
    calc++;
    calc *= 10;
    std::chrono::milliseconds dur(5000);
    std::this_thread::sleep_for(dur);
    int result = calc;
    tmmp.set_value(result);
    return;
}
void mythread2(future<int> tmmp)
{
    auto k = tmmp.get();
    cout << "thread2 " << k << endl;
    return;
}
int main()
{
    cout << "mainthreadid" << std::this_thread::get_id() << endl;
    promise<int> myprom;
    thread t1(mythread, ref(myprom), 12);
    t1.join();
    thread t2(mythread2,myprom.get_future());
    t2.join();
    return 0;
}

future其他成员函数,shared_future atomic

future_status 有三种状态:timeout ready deferred

class A
{
public:
    int mythread(int k)
    {
        cout << "threadid" << std::this_thread::get_id() << endl;
        cout << k << endl;
        std::chrono::milliseconds dura(5000);
        std::this_thread::sleep_for(dura);
        return 5;
    }
};
int main()
{
    int tm = 12;
    class A a;
    cout << "mainthreadid" << std::this_thread::get_id() << endl;
    future<int>result = std::async( &A::mythread, &a, tm);
    //future<int>result = std::async(std::launch::deferred, &A::mythread, &a,tm);
    cout << ".........." << endl;
   // cout << result.get() << endl;   //如果线程没执行完,线程会卡在这里等待线程执行完毕返回结果。
    future_status status = result.wait_for(std::chrono::seconds(6));
    if (status == future_status::timeout)  //超时,指的的等待时间结束后,线程还没有运行完
    {
        cout << "超时,线程还没有执行完" << endl;
    }
    else if (status == future_status::ready) // 准备好,表示等待时间结束后,线程已经运行完了
    {
        cout << "线程已经执行完毕" << endl;
    }
    else if (status == future_status::deferred) //如果async第一个参数是deferred,该条件成立
    {
        cout << "线程被延迟执行" << endl;
        cout << result.get() << endl;
    }
    cout << "!!!!!!" << endl;
    return 0;
}

std::shared_future (主要是为了解决future_get()只能被调用一次的问题(这里的get其实这里是转移的意思),适合用于多个线程使用同一个future,本质就是把get()的语义由转移变为拷贝)

int mythread(int k)
{
    cout << "threadid" << std::this_thread::get_id() << endl;
    std::chrono::milliseconds dura(5000);
    std::this_thread::sleep_for(dura);
    return 5;
}
int mythread2(shared_future<int>& g)
{
    cout << "get_threadid" << std::this_thread::get_id() << endl;
    cout << g.get() << endl;
    return 5;
}
int main()
{
    cout << "mainthreadid" << std::this_thread::get_id() << endl;
    packaged_task<int(int)>mypt(mythread);
    thread t1(ref(mypt), 1);  //ref()指用引用的方式传递
    t1.join();
    std::future<int> result = mypt.get_future();         
    std::shared_future<int>result2(move(result));//或者 std::shared_future<int>result2(result.share());
    //或者直接std::shared_future<int>result2(mypt.get_future());
    if (result.valid())  //可用于判断result是否可以被get,就是是否为空 
    {
        cout << "result有效" << endl;
    }
    thread t2(mythread2, ref(result2));  //ref()指用引用的方式传递    
    thread t3(mythread2, ref(result2));  //ref()指用引用的方式传递
    thread t4(mythread2, ref(result2));  //ref()指用引用的方式传递
    t2.join();
    t3.join();
    t4.join();
    cout << "!!!!!" << endl;
    return 0;
}

原子操作:: std::atomic(比加锁效率高,频繁加锁影响效率)
原子操作可以理解为一种不需要互斥量加锁(无锁)技术的多线程并发编程方式
原子操作:在多线程中不会被打断的程序执行片段
原子操作和互斥量的使用场景不同(原子:即不可分割)
互斥量加锁一般针对一个代码段,而原子操作针对的一般都是一个变量,而不是一个代码段
一般用来处理变量的简单操作
注意:一般的atomic原子操作,针对++,–,+=,&=,|=,^=,-=是支持的。其他的可能不支持,遇到这种情况,可以写一小段代码进行测试
原子操作不支持拷贝构造函数
例如:

以下这些都是不被允许的
atomic<>atm=0;
atomic<int>atm2=atm
auto<int>atm3=atm
若实在想复制,可以调用load()函数
load():以原子的方式读atomic的值
atomic<int>atm2(atm.load())
auto<int>atm3(atm.load())
store():以原子方式写入内容
atm2.store(12)  //atm2=12

下面将用互斥量和锁进行一个对比,并结合c++时间戳来比较两者之间的效率差距

//使用原子操作需要时间
#include <iostream>
#include<thread>
#include<mutex>
#include<list>
#include<Windows.h>
using namespace std;
atomic<int> m_count = 0;
void mythread()
{
    for (int i = 0; i < 20000000; i++)
    {
        m_count++;
    }
}
int main()
{
    auto tpbegin = chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now().time_since_epoch());
    thread t1(mythread);
    thread t2(mythread);
    t1.join();
    t2.join();
    auto tpend = chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now().time_since_epoch());
    cout <<"累计 40000000所需时间:"<< tpend.count()- tpbegin.count() <<"ms"<< endl;
    cout << m_count << endl;
    return 0;
}
累计 40000000所需时间:1098ms
40000000

//使用互斥量需要时间
#include <iostream>
#include<thread>
#include<mutex>
#include<list>
#include<Windows.h>

using namespace std;
int m_count = 0;
mutex m_gmutex;
void mythread()
{
    for (int i = 0; i < 20000000; i++)
    {
        m_gmutex.lock();
        m_count++;
        m_gmutex.unlock();
    }
}
int main()
{
    auto tpbegin = chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now().time_since_epoch());
    thread t1(mythread);
    thread t2(mythread);
    t1.join();
    t2.join();
    auto tpend = chrono::duration_cast<chrono::milliseconds>(chrono::high_resolution_clock::now().time_since_epoch());
    cout <<"累计 40000000所需时间:"<< tpend.count()- tpbegin.count() <<"ms"<< endl;
    cout << m_count << endl;
    return 0;
}

累计 40000000所需时间:6408ms
40000000

综合上述,在累计40000000操作后,原子操作比互斥操作快6倍左右,随着次数越来越多,差距会越大。

深入谈谈async

async有两个参数
std::launch::deferred【延迟调用】
std::launch::async【强制创建一个线程】
用thread就说明创建线程,对于async来说,一般不叫它创建线程(虽然效果是能够创建线程),一般叫它为创建一个异步任务
async和thread最明显的不同是,async有时候并不创建线程(用std::launch::deferred参数的时候)
(a)当使用std::launch::deferred参数的时候,并不会创建新的线程,他会延迟到future对象调用.get()或.wait()的时候才执行入口函数,如果没 有调用.get()或.wait(),甚至连入口函数都不执行
(b)当使用std::launch::async时候,会强制这个异步任务在新线程上面执行,意味着,系统必须要创建出一个新线程来运行入口函数
©当使用std::launch::deferred|std::launch::async,系统会根据情况自己选择一种执行
(d)若不使用参数,效果和std::launch::deferred|std::launch::async一样的,系统会自行决定会同步调用还是异步调用

所谓的自行决定是异步(创建新线程)还是同步(不创建新线程)方式运行
(1)async和thread区别
(a)使用thread方式创建线程,不容易拿到线程的返回值(要拿可能需要使用全局变量的方式)
使用async创建异步任务,可以很轻松的通过future获得线程的返回值
(b)用thread创建线程,如果系统资源紧张,有可能会创建线程失败,容易导致程序崩溃。
使用async一般不会崩溃,因为如果系统资源紧张的时候,async这种不加额外参数的调用就不会创建新线程,而是后续调用result.get()来请求结果,判断是同步还是异步执行。
经验:一个程序里面,线程数量不宜超过100-200
(2)当使用async,不用参数的时候那如果判断是同步还是异步,需要使用future_status来判断


    future_status status = result.wait_for(std::chrono::seconds(0));
    if (status == future_status::timeout)  //超时,指的的等待时间结束后,线程还没有运行完
    {
        cout << "超时,线程还没有执行完" << endl;
    }
    else if (status == future_status::ready) // 准备好,表示等待时间结束后,线程已经运行完了
    {
        cout << "线程已经执行完毕" << endl;
    }
    //上面两种情况说明都是异步执行,从async创建时候线程就执行了
    else if (status == future_status::deferred) //如果async第一个参数是deferred,该条件成立
    {
        cout << "线程被延迟执行" << endl;
        cout << result.get() << endl;   //如果是这种情况,只有当.get()是函数才开始跑,且在同一个线程里面执行。
    }

window临界区

#include <iostream>
#include<thread>
#include<mutex>
#include<list>
#include<Windows.h>
using namespace std;
#define _WINDOWSJQ_   //开关

class A {
public:
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100; i++)
        {
            cout << "inmsgRecvqueue执行,插入一个元素" << i << endl;
#ifdef _WINDOWSJQ_
            EnterCriticalSection(&my_winsec);
             msgRecvQueue.push_back(i);
            LeaveCriticalSection(&my_winsec);
#else
            unique_lock<mutex> guard1(my_mutex);
            msgRecvQueue.push_back(i);
#endif // _WINDOWSJQ_
        }
    }
    void outMsgRecvQueue()
    {
        while (1)
        {
            if (!msgRecvQueue.empty())
            {
                if (!msgRecvQueue.empty())
                {
#ifdef _WINDOWSJQ_
                    EnterCriticalSection(&my_winsec);
                    int k = msgRecvQueue.front();
                    cout << "取出" << k << endl;
                    msgRecvQueue.pop_front();
                    LeaveCriticalSection(&my_winsec);
#else
                    unique_lock<mutex> guard1(my_mutex);
                    int k = msgRecvQueue.front();
                    cout << "取出" << k << endl;
                    msgRecvQueue.pop_front();
#endif // _WINDOWSJQ_   
                }
            } 
           
        }
    }
    A()
    {
#ifdef _WINDOWSJQ_
        InitializeCriticalSection(& my_winsec);//windows中的临界区使用必须初始化 
#endif // _WINDOWSJQ_

    }
public:
    list<int>msgRecvQueue;
    mutex my_mutex;
    condition_variable my_cond;

#ifdef _WINDOWSJQ_
    CRITICAL_SECTION my_winsec;//windows中的临界区,非常类似于互斥量mutex
#endif // _WINDOWSJQ_

};
int main()
{
    A myojia;
    std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
    std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
    myOutmsgobj.join();
    myInmsgobj.join();
    return 0;
}

在同一线程中,windows中可以多次进入临界区。但是调用几次临界区,就需要调用出几次临界区。而在c++11中,不允许同一个线程lock一个互斥量多次,否则报异常。

自动析构技术

下面把win临界区封装成一个类,本类自动释放临界区,类似mutex的lock_guard
这种叫做RAII类,即(Resource Acquisition is initialization) “资源获取即初始化”,容器,智能指针都是raii类

#include <iostream>
#include<thread>
#include<mutex>
#include<list>
#include<Windows.h>
using namespace std;
#define _WINDOWSJQ_   //开关

//本类自动释放临界区,类似mutex的lock_guard
//这种叫做RAII类,即(Resource Acquisition is initialization) “资源获取即初始化”,容器,智能指针都是raii类
class winlock
{
public:
    winlock(CRITICAL_SECTION* winsec)
    {
        my_winsec = winsec;
        EnterCriticalSection(my_winsec);
    }
    ~winlock()
    {
        LeaveCriticalSection(my_winsec);
    }
    CRITICAL_SECTION *my_winsec;
};
class A {
public:
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100; i++)
        {
            cout << "inmsgRecvqueue执行,插入一个元素" << i << endl;
#ifdef _WINDOWSJQ_
            winlock win(&my_winsec);
            msgRecvQueue.push_back(i);
            
#else
            unique_lock<mutex> guard1(my_mutex);
            msgRecvQueue.push_back(i);
#endif // _WINDOWSJQ_
        }
    }
    void outMsgRecvQueue()
    {
        while (1)
        {
            if (!msgRecvQueue.empty())
            {
                if (!msgRecvQueue.empty())
                {
#ifdef _WINDOWSJQ_
                    winlock win(&my_winsec);
                    int k = msgRecvQueue.front();
                    cout << "取出" << k << endl;
                    msgRecvQueue.pop_front();
#else
                    unique_lock<mutex> guard1(my_mutex);
                    int k = msgRecvQueue.front();
                    cout << "取出" << k << endl;
                    msgRecvQueue.pop_front();
#endif // _WINDOWSJQ_   
                }
            }

        }
    }
    A()
    {
#ifdef _WINDOWSJQ_
        InitializeCriticalSection(&my_winsec);//windows中的临界区使用必须初始化 
#endif // _WINDOWSJQ_

    }
public:
    list<int>msgRecvQueue;
    mutex my_mutex;
    condition_variable my_cond;

#ifdef _WINDOWSJQ_
    CRITICAL_SECTION my_winsec;//windows中的临界区,非常类似于互斥量mutex
#endif // _WINDOWSJQ_

};
int main()
{
    A myojia;
    std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
    std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
    myOutmsgobj.join();
    myInmsgobj.join();
    return 0;
}

recursive_mutex递归的独占互斥量

主要是为了解决重复lock会报错的问题
std::mutex:独占互斥量,自己lock了,别人lock不了
recursive_mutex:递归的独占互斥量:允许同一个线程,同一个互斥量多次lock。

#include <iostream>
#include<thread>
#include<mutex>
#include<list>
#include<Windows.h>
using namespace std;
//#define _WINDOWSJQ_   //开关

//本类自动释放临界区,类似mutex的lock_guard
//这种叫做RAII类,即(Resource Acquisition is initialization) “资源获取即初始化”,容器,智能指针都是raii类
class winlock
{
public:
    winlock(CRITICAL_SECTION* winsec)
    {
        my_winsec = winsec;
        EnterCriticalSection(my_winsec);
    }
    ~winlock()
    {
        LeaveCriticalSection(my_winsec);
    }
    CRITICAL_SECTION *my_winsec;
};
class A {
public:
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100; i++)
        {
            cout << "inmsgRecvqueue执行,插入一个元素" << i << endl;
#ifdef _WINDOWSJQ_
            winlock win(&my_winsec);
            msgRecvQueue.push_back(i);
            
#else
            unique_lock<recursive_mutex> guard1(my_mutext);//这里相当与lock了三次
            testfun2();
            msgRecvQueue.push_back(i);
#endif // _WINDOWSJQ_
        }
    }
    void outMsgRecvQueue()
    {
        while (1)
        {
            if (!msgRecvQueue.empty())
            {
                if (!msgRecvQueue.empty())
                {
#ifdef _WINDOWSJQ_
                    winlock win(&my_winsec);
                    int k = msgRecvQueue.front();
                    cout << "取出" << k << endl;
                    msgRecvQueue.pop_front();
#else
                    unique_lock<recursive_mutex> guard1(my_mutext);
                    int k = msgRecvQueue.front();
                    cout << "取出" << k << endl;
                    msgRecvQueue.pop_front();
#endif // _WINDOWSJQ_   
                }
            }

        }
    }
    A()
    {
#ifdef _WINDOWSJQ_
        InitializeCriticalSection(&my_winsec);//windows中的临界区使用必须初始化 
#endif // _WINDOWSJQ_

    }
public:
    list<int>msgRecvQueue;
    condition_variable my_cond;
    std::recursive_mutex my_mutext;

#ifdef _WINDOWSJQ_
    CRITICAL_SECTION my_winsec;//windows中的临界区,非常类似于互斥量mutex
#endif // _WINDOWSJQ_

    void testfun1()
    {
        lock_guard<std::recursive_mutex> sbguard(my_mutext);
        //干一些事情
    }
    void testfun2()
    {
        lock_guard<std::recursive_mutex> sbguard(my_mutext);
        //干一些事情
        testfun1();
    }
};
int main()
{
    A myojia;
    std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
    std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
    myOutmsgobj.join();
    myInmsgobj.join();
    return 0;
}

但是这种多次锁效率肯定比mutex效率差,如果通过改代码能改成只lock一次最好。

带超时的互斥量std::timed_mutex

std::timed_mutex带超时的独占互斥量
有两个参数:
try_lock_for();等待一段时间,无论有没有拿到锁,程序都走下去
try_lock_until();参数是一个未来的时间点,在这个未来的时间没到的时间内,如果拿到锁,就走下去,到时间没拿到,也走下去。
try_lock_for()示例

#include <iostream>
#include<thread>
#include<mutex>
#include<list>
#include<Windows.h>
using namespace std;
//#define _WINDOWSJQ_   //开关

//本类自动释放临界区,类似mutex的lock_guard
//这种叫做RAII类,即(Resource Acquisition is initialization) “资源获取即初始化”,容器,智能指针都是raii类
class winlock
{
public:
    winlock(CRITICAL_SECTION* winsec)
    {
        my_winsec = winsec;
        EnterCriticalSection(my_winsec);
    }
    ~winlock()
    {
        LeaveCriticalSection(my_winsec);
    }
    CRITICAL_SECTION *my_winsec;
};
class A {
public:
    void inMsgRecvQueue()
    {
        for (int i = 0; i < 100; i++)
        {
            
#ifdef _WINDOWSJQ_
            winlock win(&my_winsec);
            msgRecvQueue.push_back(i);
#else
            chrono::milliseconds timeout(100);;
            if (my_mutext.try_lock_for(timeout))//等待100ms来获取锁
            {
                cout << "inmsgRecvqueue执行,插入一个元素" << i << endl;
                msgRecvQueue.push_back(i);
                my_mutext.unlock(); //拿到锁用完了记得释放
            }
            else
            {
                //没有拿到锁休眠100ms
                chrono::milliseconds dur(100);
                std::this_thread::sleep_for(dur);
            }
#endif // _WINDOWSJQ_
        }
    }
    void outMsgRecvQueue()
    {
        while (1)
        {
            if (!msgRecvQueue.empty())
            {
                if (!msgRecvQueue.empty())
                {
#ifdef _WINDOWSJQ_
                    winlock win(&my_winsec);
                    int k = msgRecvQueue.front();
                    cout << "取出" << k << endl;
                    msgRecvQueue.pop_front();
#else
                    unique_lock<timed_mutex> guard1(my_mutext);
                    chrono::milliseconds dur(1000);
                    std::this_thread::sleep_for(dur);
                    int k = msgRecvQueue.front();
                    cout << k << endl;
                    msgRecvQueue.pop_front();
                    
#endif // _WINDOWSJQ_   
                }
            }

        }
    }
    A()
    {
#ifdef _WINDOWSJQ_
        InitializeCriticalSection(&my_winsec);//windows中的临界区使用必须初始化 
#endif // _WINDOWSJQ_

    }
public:
    list<int>msgRecvQueue;
    timed_mutex my_mutext;
#ifdef _WINDOWSJQ_
    CRITICAL_SECTION my_winsec;//windows中的临界区,非常类似于互斥量mutex
#endif // _WINDOWSJQ_

};
int main()
{
    A myojia;
    std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
    std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
    myOutmsgobj.join();
    myInmsgobj.join();
    return 0;
}

try_lock_until()示例
上面替换即可。

 chrono::milliseconds timeout(100);
            if (my_mutext.try_lock_until(chrono::steady_clock::now()+timeout))//等待100ms来获取锁
            {
                cout << "inmsgRecvqueue执行,插入一个元素" << i << endl;
                msgRecvQueue.push_back(i);
                my_mutext.unlock(); //拿到锁用完了记得释放
            }
            else
            {
                //没有拿到锁休眠100ms
                chrono::milliseconds dur(100);
                std::this_thread::sleep_for(dur);
            }

recursive_mutex也有一个类似的std::recursive_timed_mutex
可以多次加锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值