C++多线程学习05 超时锁,递归锁与共享锁

一、超时锁timed_mutex

功能:避免长时间死锁,可以记录锁获取情况,多次超时,可以记录日志,获取错误情况

在04中可以由于try_lock()不会阻塞该线程而是一直占着CPU资源,因此加入sleep_for(100ms)延时一会阻塞下该线程给其他线程一点机会,然而这的延时是调用的this_thread下的函数:

if (!mux.try_lock())
        {
    
    
            cout << "." << flush;
            this_thread::sleep_for(100ms);
            continue;
        }

也可以将延时做为锁的构造函数的参数,使用超时锁timed_mutex:

timed_mutex tmux;

void ThreadMainTime(int i)
{
    
    


    for (;;)
    {
    
    
        if (!tmux.try_lock_for(chrono::milliseconds(500)))
        {
    
     
            //如果未在规定时间内拿到锁,那么这段代码可能会出现问题,这里可以进行日志的写入,便于调试
            cout << i << "[try_lock_for timeout]" << endl;
            continue;
        }
        cout << i << "[in]" << endl;
        this_thread::sleep_for(2000ms);
        tmux.unlock();
        this_thread::sleep_for(1ms);
    }
}

同样的为了确保unlock能释放资源,最后延时一下:

创建了三个线程,每个线程尝试解锁之后先阻塞500ms
疑问:三个线程彼此detach为什么打印的不会乱呢?

int main(int argc, char* argv[])
{
    
    
 
    for (int i = 0; i < 3; i++)
    {
    
    
        thread th(ThreadMainTime, i + 1);
        th.detach();
    }
    getchar();
  }

[in]表示线程进去了
[try_lock_for timeout]表示进入失败
在这里插入图片描述

二、递归锁(可重入锁)recursive_mutex

同一个线程中的同一把锁可以锁多次。避免了一些不必要的死锁
使用场景:一个函数被递归调用,而且函数访问共享数据上锁,在解锁之前递归调用自己,会用到递归锁。另一个场景,一个类,两个成员函数f1,f2都会访问共享数据,会上锁。在f1中上锁后调用f2,也需要递归锁。言外之意,只要解锁之前又调用了同一把锁的上锁操作,为了防止死锁,就需要使用递归锁。

参考:https://www.zhihu.com/question/448190927/answer/1768286353

recursive_mutex rmux;
void Task1()
{
    
    
    rmux.lock();
    cout << "task1 [in]" << endl;
    rmux.unlock();
}
void Task2()
{
    
    
    rmux.lock();
    cout << "task2 [in]" << endl;
    rmux.unlock();
}
void ThreadMainRec(int i)
{
    
    
    for (;;)
    {
    
    
        rmux.lock(); 
        Task1();
        cout << i << "[in]" << endl;
        this_thread::sleep_for(2000ms);
        Task2();
        rmux.unlock();
        this_thread::sleep_for(1ms);
    }
}

就算是递归锁也是加锁几次就要解锁几次

int main(int argc, char* argv[])
{
    
    
    
    for (int i = 0; i < 3; i++)
    {
    
    
        thread th(ThreadMainRec, i + 1);
        th.detach();
    }
    getchar();
 }  

可以看到123号ThreadMainRec交替切换,每个ThreadMainRec执行一次task1与task2
在这里插入图片描述

三、共享锁shared_mutex

经常会遇到这样的场景:
很多(100)个线程同时读一个资源,不会有问题,当有线程对这个资源修改时,这100个资源都要等待,此时再有其他资源想对这个资源修改也同样需要等待

总结下:
读资源的线程在读时其他线程可以读,但是不能写
写资源的线程在写时其他线程既不能读也不能写
因此不同需要的线程使用不同的锁,读的线程使用读的锁,写的线程使用写的锁
某个线程只读的话就用读的锁
某个线程准备写的话先用读的锁,再用写的锁,然后再对资源进行修改

现在祭出这两把锁:
c++14 共享超时互斥锁 shared_timed_mutex
c++17 共享互斥 shared_mutex

以C++14标准为例:
shared_timed_mutex stmux;
使用写的锁:stmux.lock();
使用读的锁:stmux.lock_shared();

shared_timed_mutex stmux;

void ThreadRead(int i)
{
    
    
    for (;;)
    {
    
    
        stmux.lock_shared();
        cout << i << " Read" << endl;
        //this_thread::sleep_for(500ms);
        stmux.unlock_shared();
        this_thread::sleep_for(1ms);
    }
}
void ThreadWrite(int i)
{
    
    
    for (;;)
    {
    
    
        stmux.lock(); //互斥锁 写入
        cout << i << " Write" << endl;
        this_thread::sleep_for(300ms);
        stmux.unlock();
        this_thread::sleep_for(1ms);
    }
}
int main(int argc, char* argv[])
{
    
    
    for (int i = 0; i < 3; i++)
    {
    
    
        thread th(ThreadWrite, i + 1);
        th.detach();
    }
    for (int i = 0; i < 3; i++)
    {
    
    
        thread th(ThreadRead, i + 1);
        th.detach();
    }
    getchar();
    return 0;
}

想拿到写的锁,得先确保读的锁释放与写的锁释放
想拿到读的锁,只用确保写的锁的释放
可以看到读线程经常出现在同一行,三个读线程通过for循环创建之后,如果资源没有被写的锁锁住,三个线程都能拿的读的锁,一起进入cout的线程任务中,当一个线程的cout << i << " Read" << endl;没有运行完时(汇编之后不止一行)另一个线程的cout << i << " Read" << endl;送到CPU上运行,因此会出现同一行的现象,而写线程就不会这样,他要确保当前没有读线程也没有线程
在这里插入图片描述
shared_lock C++14 实现可移动的共享互斥体所有权封装器:

int main(int argc, char* argv[])
{
    
    

    {
    
    
        //共享锁
        static shared_timed_mutex  tmux;
        //读取锁 共享锁
        {
    
    
            //调用共享锁 
            shared_lock<shared_timed_mutex> lock(tmux);
            cout << "read data" << endl;
            //退出栈区 释放共享锁
        }
        //写入锁 互斥锁
        {
    
    
            unique_lock<shared_timed_mutex> lock(tmux);
            cout << "write data" << endl;
        }
    }
    return 0;
}

shared_lock<shared_timed_mutex> lock(tmux);在构造函数中对传入的互斥量上共享锁

explicit shared_lock(mutex_type& _Mtx)
        : _Pmtx(_STD addressof(_Mtx)), _Owns(true) {
    
     // construct with mutex and lock shared
        _Mtx.lock_shared();
    }

unique_lock<shared_timed_mutex> lock(tmux);在构造函数中对传入的互斥量上互斥锁

explicit unique_lock(_Mutex& _Mtx) : _Pmtx(_STD addressof(_Mtx)), _Owns(false) {
    
     // construct and lock
        _Pmtx->lock();
        _Owns = true;
    }

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42567607/article/details/125530759