并发与多线程(3)共享数据、Mutex、lock 、unique_lock

一、有读有写

共享数据的保护案例:

网络游戏服务 有两个线程:一个线程用来收集玩家命令,并把数据写到一个  ;一个线程从队列中取出玩家需要的动作。

用成员函数来作为线程函数的方法来写线程:

保护共享数据问题得第一个概念: 互斥量,保护共享数据,操作的时候 摸个线程用代码将共享数据锁住,操作数据,解锁,在锁的同时其他共享数据的线程都在等待。

互斥量(mutex):mutex 是一个类对象,可以理解成一把锁,当多个线程尝试用lock() 成员函数来加锁的时候,只有一个线程能锁定成功,如果没有锁成功,那么流程卡在lock () 这里不断的尝试去锁。

互斥量使用的是要小心,保护的数据不能太多也不能太少

互斥量的使用: lock  unlock成对使用,并且每个互斥量就是一把锁,如果有两个互斥量,那就是两把锁



#if 1   // 有读有写  2个线程在读 8个线程在写
class  A
{
public :
    // 吧收到的消息 入到一个队列当中
    void  inMsgRecvQueue()
    {
        for (size_t i = 0; i < 10000; i++)
        {

            cout << "inMsgRecvQueue() 执行,插入一个元素  " << i << endl;

            // 用互斥量来加锁
             // ==================================
            my_mutex.lock();
            msgRecvQueue.push_back(i);
            my_mutex.unlock();
            // ==================================

        }
    }

    bool  outMSG(  int  &cmd)
    {

        // ==================================
        my_mutex.lock();
        if (msgRecvQueue.empty()==false)
        {
            cmd = msgRecvQueue.front();
            msgRecvQueue.pop_front();
            my_mutex.unlock();
            // ==================================
            return  true;
        }
        my_mutex.unlock();

        // ==================================
        return  false;
    }
    // 另外一个线程
    void  getMsgRecvQueue()
    {
        int cmd=0;
        for (size_t i = 0; i < 10000; i++)
        {
            bool  result  = outMSG(cmd);
            if (result==true)
            {
                cout << " getMsgRecvQueue(): sss   " << cmd<< endl;
            }
            else
            {
                cout<<" getMsgRecvQueue():   是空的  "  << endl;
            }
        }
    }
private:
    list msgRecvQueue;
    mutex my_mutex; // 创建一个互斥量
     
};

int main()
{

    A a;
    thread mythread(&A::getMsgRecvQueue,&a); // 第二对象是 & 
    thread mythread2(&A::inMsgRecvQueue, &a);
     mythread.join();
    mythread2.join();
    cout << "Hello World!\n";
}

#endif

二、死锁

多个(至少两个)锁头之间发生的相互等待的情况。

 案例分析: 有连个锁 lock 1  lock 2,有两个线程A  B

A线程:执行的时候lock1先锁(不解锁)然后 lock 2 上锁。

B线程:先lock2  上锁后 (不解锁),然后去lock1 

结果就是: A线程 要用lock 2 ,但是lock 2 被B线程锁定

                   B线程 要用lock 1 ,但是lock1被A 线程锁定

#if 1   //死锁 形成
class  A
{
public:
    // 吧收到的消息 入到一个队列当中
    void  inMsgRecvQueue()
    {
        for (size_t i = 0; i < 10000; i++)
        {

            cout << "inMsgRecvQueue() 执行,插入一个元素  " << i << endl;

            // 用互斥量来加锁
             // ==================================
            // in线程先lock 1  (不解锁),然后接着lock2
            my_mutex.lock();
            my_mutex2.lock();

            msgRecvQueue.push_back(i);


            my_mutex.unlock();
            my_mutex2.unlock();
            // ==================================

        }
    }

    bool  outMSG(int& cmd)
    {

        // ==================================
         // out   线程 先锁 2 不解锁,直接lock 1 ,
        my_mutex2.lock();
        my_mutex.lock();
        if (msgRecvQueue.empty() == false)
        {
            cmd = msgRecvQueue.front();
            msgRecvQueue.pop_front();

            my_mutex.unlock();
            my_mutex2.unlock();
            // ==================================
            return  true;
        }
        my_mutex.unlock();
        my_mutex2.unlock();


        // ==================================
        return  false;
    }
    // 另外一个线程
    void  getMsgRecvQueue()
    {
        int cmd = 0;
        for (size_t i = 0; i < 10000; i++)
        {
            bool  result = outMSG(cmd);
            if (result == true)
            {
                cout << " getMsgRecvQueue(): sss   " << cmd << endl;
            }
            else
            {
                cout << " getMsgRecvQueue():   是空的  " << endl;
            }
        }
    }
private:
    list msgRecvQueue;
    mutex my_mutex; // 创建一个互斥量
    mutex my_mutex2; // 创建一个互斥量
};

int main()
{

    A a;
    thread mythread(&A::getMsgRecvQueue, &a); // 第二对象是 & 
    thread mythread2(&A::inMsgRecvQueue, &a);
    mythread.join();
    mythread2.join();
    cout << "Hello World!\n";
}

#endif

死锁的解决方法: 多线程中锁的顺序保持一致 

lock的使用:可以锁多个互斥量,为了解决一些能锁住,一些锁不住的问题。

        lock(my_mutex, my_mutex2);

​​​​​​​


#if 1   //lock
class  A
{
public:
    // 吧收到的消息 入到一个队列当中
    void  inMsgRecvQueue()
    {
        for (size_t i = 0; i < 10000; i++)
        {

            cout << "inMsgRecvQueue() 执行,插入一个元素  " << i << endl;

            // 用互斥量来加锁
             // ==================================
            // in线程先lock 1  (不解锁),然后接着lock2
         //   my_mutex.lock();
          //  my_mutex2.lock();
            lock(my_mutex, my_mutex2);
            msgRecvQueue.push_back(i);


            my_mutex.unlock();
            my_mutex2.unlock();
            // ==================================

        }
    }

    bool  outMSG(int& cmd)
    {

        // ==================================
         // out   线程 先锁 2 不解锁,直接lock 1 ,
        //my_mutex2.lock();
        //my_mutex.lock();

        lock(my_mutex, my_mutex2);
        if (msgRecvQueue.empty() == false)
        {
            cmd = msgRecvQueue.front();
            msgRecvQueue.pop_front();

            my_mutex.unlock();
            my_mutex2.unlock();
            // ==================================
            return  true;
        }
        my_mutex.unlock();
        my_mutex2.unlock();


        // ==================================
        return  false;
    }
    // 另外一个线程
    void  getMsgRecvQueue()
    {
        int cmd = 0;
        for (size_t i = 0; i < 10000; i++)
        {
            bool  result = outMSG(cmd);
            if (result == true)
            {
                cout << " getMsgRecvQueue(): sss   " << cmd << endl;
            }
            else
            {
                cout << " getMsgRecvQueue():   是空的  " << endl;
            }
        }
    }
private:
    list msgRecvQueue;
    mutex my_mutex; // 创建一个互斥量
    mutex my_mutex2; // 创建一个互斥量
};

int main()
{

    A a;
    thread mythread(&A::getMsgRecvQueue, &a); // 第二对象是 & 
    thread mythread2(&A::inMsgRecvQueue, &a);
    mythread.join();
    mythread2.join();
    cout << "Hello World!\n";
}

#endif

 adopt_lock  是一个结构体对象,起一个标价的作用,表示这个互斥量已经被锁定

三、unique_lock的第二个参数

unique_lock 是一个类模板,在工作中,一般lock_guard (推荐使用,其取代了mutex.lock  /unlock) 

unique_lock比较灵活,同时占用的内存也比较多,运行时间也比较长。

lock_guard的使用:

adopt_lock

前必须要lock()

lock_guard mylock(my_mutex, adopt_lock);  // 第二个参数 其一个标记的作用,表示这个mutex 已经被锁定

unique_lock 的使用,也可以是adopt_lock,其意思就是这个已经被上锁,不希望再次上锁;

try_to_lock

不能自己lock()

unique_lock mylock2(my_mutex,try_to_lock);

/ unique_lock mylock2(my_mutex,try_to_lock);  的功能就是:我们会去尝试用lock 去锁定这个mutex,如果没有锁定成功我们将立即返回,不会阻塞到哪里,用try_to_lock 的前提是不能自己去上锁

 


#if 1   //unique_lock

// unique_lock mylock2(my_mutex,try_to_lock);  的功能就是:我们会去尝试用lock 去锁定这个mutex,如果没有锁定成功我们将立即返回,不会阻塞到哪里,用try_to_lock 的前提是不能自己去上锁
class  A
{
public:
    // 吧收到的消息 入到一个队列当中
    void  inMsgRecvQueue()
    {
        for (size_t i = 0; i < 10000; i++)
        {

            cout << "inMsgRecvQueue() 执行,插入一个元素  " << i << endl;
            unique_lock mylock2(my_mutex, try_to_lock);  // try_to_lock 尝试锁定这个互斥量mutex
            if (mylock2.owns_lock())  // 如果已经锁定, 处理数据,没有锁定直接返回
            {
                msgRecvQueue.push_back(i);
            }
            else
            {
                cout << "inMsgRecvQueue() 执行,但是没有拿到锁=====================================================================  "  << endl;
            }

            // ==================================

        }
    }

    bool  outMSG(int& cmd)
    {

        // ==================================
         // out   线程 先锁 2 不解锁,直接lock 1 ,
        //my_mutex2.lock();
        //my_mutex.lock();
        //   lock_guard mylock(my_mutex, adopt_lock);  // 第二个参数 其一个标记的作用,表示这个mutex 已经被锁定  ,
       // my_mutex.lock();
        unique_lock mylock2(my_mutex,try_to_lock);
        chrono::microseconds d(200);
        this_thread::sleep_for(d);
        if (msgRecvQueue.empty() == false)
        {
            cmd = msgRecvQueue.front();
            msgRecvQueue.pop_front();
            // ==================================
            return  true;
        }
 
        // adopt_lock  是一个结构体对象,起一个标价的作用,表示这个互斥量已经被锁定
       // ==================================
        return  false;
    }
    // 另外一个线程
    void  getMsgRecvQueue()
    {
        int cmd = 0;
        for (size_t i = 0; i < 10000; i++)
        {
            bool  result = outMSG(cmd);
            if (result == true)
            {
                cout << " getMsgRecvQueue(): sss   " << cmd << endl;
            }
            else
            {
                cout << " getMsgRecvQueue():   是空的  " << endl;
            }
        }
    }
private:
    list msgRecvQueue;
    mutex my_mutex; // 创建一个互斥量
};

int main()
{

    A a;
    thread mythread(&A::getMsgRecvQueue, &a); // 第二对象是 & 
    thread mythread2(&A::inMsgRecvQueue, &a);
    mythread.join();
    mythread2.join();
    cout << "Hello World!\n";
}

#endif

 defer_lock

必须要自己加锁:

四、unique_lock的成员函数

1、lock()

2、unlock()

3、try_lock(),我们尝试去加锁,如果成功就返回true ,如果不成功就返回FALSE

4、release() ,返回她所管理的mutex对象的指针,并释放所有权,

你可能感兴趣的:(C++,c++)