C++11多线程使用互斥变量
简介
在学习操作系统的时候,有学过互斥变量,也就是用来保护原子数在同一时刻只能被一个线程进行访问和修改。C++中通过实例化 std::mutex 创建互斥量,通过调用成员函数lock()进行上锁,unlock()进行解锁。不过,不推荐实践中直接去调用成员函数,因为调用成员函数就意味着,必须记住在每个函数出口都要去调用unlock(),也包括异常的情况。C++标准库为互斥量提供了一个RAII语法的模板类 std::lock_guard ,其会在构造的时候提供已锁的互斥量,并在析构的时候进行解锁,从而保证了一个已锁的互斥量总是会被正确的解锁
不加锁的情况
#include <iostream>
#include <string>
#include <thread>
using namespace std;
void function1()
{
for(int i = 0; i < 10; i++)
{
cout << "From t:" << i << endl;
}
}
int main()
{
thread t(function1);
for(int i = 0; i > -10; i--)
{
cout << "From main:" << i << endl;
}
t.join();
return 0;
}
在不加锁的情况下,主进程main()和子进程t同时共用cout
导致很混乱
使用lock()和unlock()进行加锁和解锁
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
using namespace std;
mutex mu; //互斥变量,全局变量
void share_print(string msg, int id)
{
mu.lock();
cout << msg << id << endl;
mu.unlock();
}
void function1()
{
for(int i = 0; i < 10; i++)
{
//cout << i << endl;
share_print("From t:", i);
}
}
int main()
{
thread t(function1);
for(int i = 0; i > -10; i--)
{
//cout << i << endl;
share_print("From main:", i);
}
t.join();
return 0;
}
可以清楚的看到,加了锁后,输出就有序了。但是这样还存在一个问题,若在解锁
unlock()
之前出现了异常,那就永远不会解锁,及cout
就永远不会被释放,因此一般不使用lock()
和unlock
使用lock_guard进行互斥
std::lock_guard ,其会在构造的时候提供已锁的互斥量,并在析构的时候进行解锁,从而保证了一个已锁的互斥量总是会被正确的解锁
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
using namespace std;
mutex mu;
void share_print(string msg, int id)
{
//mu.lock();
lock_guard<mutex> guard(mu);
cout << msg << id << endl;
//mu.unlock();
}
void function1()
{
for(int i = 0; i < 10; i++)
{
//cout << i << endl;
share_print("From t:", i);
}
}
int main()
{
thread t(function1);
for(int i = 0; i > -10; i--)
{
//cout << i << endl;
share_print("From main:", i);
}
t.join();
return 0;
}
加锁后有序了
总结
- std::mutex 和 std::lock_guard都在 头文件中声明。
- mutex必须为全局变量
- 若在解锁
unlock()
之前出现了异常,则被保护的变量将永远不会被释放,因此一般不用lock()
和unlock()