对于下面10个子线程,不上锁时:
void test()
{
std::cout<<"当前线程id:"<<std::this_thread::get_id()<<std::endl;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
std::thread* threads[10];
for (int i = 0; i < 10; ++i) {
threads[i] = new std::thread(test);
}
for (int i = 0; i < 10; ++i) {
threads[i]->join();
delete threads[i];
}
}
输出的结果很乱,原因是因为不同线程同时运行
当用到mutex锁后:
std::mutex mtx;
void test()
{
mtx.lock();
std::cout<<"当前线程id:"<<std::this_thread::get_id()<<std::endl;
mtx.unlock();
}
结果变的很整齐,原因是因为上了锁后的线程,会等待当前线程结束后才会接着运行下一个线程。
死锁
考虑这样一个情况:假设线程1上锁成功,线程2上锁等待。但是线程1上锁成功后,抛出异常并退出,没有来得及释放锁,导致线程2“永久的等待下去”(线程2:我的心在等待永远在等待……),此时就发生了死锁。
std::mutex mtx;
void test()
{
mtx.lock();
std::cout<<"当前线程id:"<<std::this_thread::get_id()<<std::endl;
try
{
throw "抛出异常";
}
catch (...) // ...表示捕获所有异常
{
std::cout<<"出现异常"<<std::endl;
return;
}
mtx.unlock();
}
结果:
为解决这个问题可以在每一个函数返回时加上unlock()函数,例如:
std::mutex mtx;
void test()
{
mtx.lock();
std::cout<<"当前线程id:"<<std::this_thread::get_id()<<std::endl;
try
{
throw "抛出异常";
}
catch (...) // ...表示捕获所有异常
{
std::cout<<"出现异常"<<std::endl;
mtx.unlock();
return;
}
mtx.unlock();
}
结果为
但是这种操作比较麻烦,很容易遗漏。这个时候就需要std::lock_guard模板类登场了。std::lock_guard只有构造函数和析构函数。简单的来说:当调用构造函数时,会自动调用传入的对象的lock()函数,而当调用析构函数时,自动调用unlock()函数。这样一来,只要超出了mutex的作用域就会自动解锁。
std::mutex mtx;
void test()
{
std::lock_guard<std::mutex> Myloc(mtx); //模板类
std::cout<<"当前线程id:"<<std::this_thread::get_id()<<std::endl;
try
{
throw "抛出异常";
}
catch (...) // ...表示捕获所有异常
{
std::cout<<"出现异常"<<std::endl;
return;
}
}
结果: