功能:避免长时间死锁,可以记录锁获取情况,多次超时,可以记录日志,获取错误情况
在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]表示进入失败
同一个线程中的同一把锁可以锁多次。避免了一些不必要的死锁
组合业务 用到同一个锁
如果组合业务使用的是同一个锁,但是不是递归锁,就需要在进入这些线程前要先加锁再解锁,这个加锁解锁的间隔cpu的资源可能被其他线程占着了
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
经常会遇到这样的场景:
很多(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上运行,因此会出现同一行的现象,而写线程就不会这样,他要确保当前没有读线程也没有线程