1. 线程间数据共享(只读)
如果线程之间共享的数据是只读数据,也就是所有的线程都只读取共享数据而不去修改里面的数据,则该数据不需要额外的数据保护策略,该只读数据为线程安全的。
#include <iostream>
#include <thread>
#include <iostream>
#include <vector>
// #include <mutex>
using namespace std;
class ThreadTest {
public:
void InMsg(int n) {
for(int i = 0; i < n; i ++) {
for(int i = 0; i < my_list_.size(); ++ i) {
cout << my_list_[i] << " ";
}
cout << endl;
}
}
private:
vector<int> my_list_ = {1,2,3,4,5,6,7,8,9,10};
// mutex mutex_;
};
int main() {
ThreadTest a;
int n = 10000;
thread t1(&ThreadTest::InMsg, &a, n); // 开启线程1
thread t2(&ThreadTest::InMsg, &a, n); // 开启线程2
t1.join();
t2.join();
return 0;
}
2. 共享数据有读有写
如果线程共享数据有读有写,则该数据线程不安全,下面展示的程序为线程不安全,其多次运行必定会出现core dumped错误。
#include <iostream>
#include <thread>
#include <iostream>
#include <vector>
#include <mutex>
using namespace std;
class ThreadTest {
public:
void OutMsg(int n) {
for(int i = 0; i < n; i ++) {
my_list_.push_back(i);
cout << "push number is:" << my_list_.back() << endl;
}
}
bool PopMsg(int &cmd) {
if (!my_list_.empty()) {
cmd = my_list_.front();
my_list_.erase(my_list_.begin(), my_list_.begin() + 1);;
return true;
}
return false;
}
void InMsg(int n) {
for(int i = 0; i < n; i ++) {
int cmd = 0;
if (PopMsg(cmd)) {
cout << "pop msg is:" << cmd << endl;
} else {
cout << "list is empty!!" << endl;
}
}
}
private:
vector<int> my_list_;
};
int main() {
ThreadTest a;
int n = 10000;
thread t1(&ThreadTest::OutMsg, &a, n);
thread t2(&ThreadTest::InMsg, &a, n);
t1.join();
t2.join();
return 0;
}
上面程序线程不安全,主要原因是由于各线程之间对共享数据进行了读写,注意只要有一个线程对其进行了写操作,其就是不安全的。
c++11提供了std::mutex类,表示互斥量,来保护共享数据,当某个线程A调用mutex的成员函数lock()成功之后,其他线程都将堵塞到lock()函数,直到A线程释放了该互斥锁(也就是调用了成员函数unlock())之后,其他各线程再次竞争获得互斥量,也就是谁lock()成功,然后其他线程再次被阻塞。该互斥量以阻塞的方式进行数据的保护,因此会牺牲一定的性能。将上述代码进行加锁得到如下代码:
#include <iostream>
#include <thread>
#include <iostream>
#include <vector>
#include <mutex>
using namespace std;
class ThreadTest {
public:
void OutMsg(int n) {
// std::chrono::microseconds dura(1);
for(int i = 0; i < n; i ++) {
my_mutex_.lock(); // 加锁
my_list_.push_back(i);
cout << i << "push number is:" << my_list_.back() << endl;
// if(i != my_list_.back()) {
// cout << "==================================" << endl;
// }
my_mutex_.unlock(); // 解锁
}
}
bool PopMsg(int &cmd) {
my_mutex_.lock(); // 加锁
if (!my_list_.empty()) {
cmd = my_list_.front();
my_list_.erase(my_list_.begin(), my_list_.begin() + 1);
my_mutex_.unlock(); // 解锁(如果忘记就会一直卡到这里)
return true;
}
my_mutex_.unlock(); // 解锁
return false;
}
void InMsg(int n) {
for(int i = 0; i < n; i ++) {
// std::chrono::microseconds dura(1);
// std::this_thread::sleep_for(dura);
int cmd = 0;
if (PopMsg(cmd)) {
cout << "pop msg is:" << cmd << endl;
} else {
cout << "list is empty!!" << endl;
}
}
}
private:
vector<int> my_list_;
mutex my_mutex_;
};
int main() {
ThreadTest a;
int n = 100000;
thread t1(&ThreadTest::OutMsg, &a, n);
thread t2(&ThreadTest::InMsg, &a, n);
t1.join();
t2.join();
return 0;
}
std::lock_guard模板类
在实际的开发过程中,我们很容易忘记lock互斥量之后手动的unlock,或者程序有多个返回点,很难写出一个lock对应一个unlock,因此c++11给出了lock_guard模板类来管理互斥量的加锁和解锁。该模板类在构造函数中调用绑定互斥量的lock()成员函数,在析构函数中调用绑定互斥量的unlock函数,因此,不需要手动去加锁和释放锁。因此上述程序可以写成如下方式:
#include <iostream>
#include <thread>
#include <iostream>
#include <vector>
#include <mutex>
using namespace std;
class ThreadTest {
public:
void OutMsg(int n) {
// std::chrono::microseconds dura(1);
for(int i = 0; i < n; i ++) {
{
std::lock_guard<std::mutex> lkgd(my_mutex_);// 构造函数中调用了my_mutex_的lock()成员函数
my_list_.push_back(i);
cout << i << "push number is:" << my_list_.back() << endl;
} // 执行完该程序块之后,调用lkgd的析构函数,在析构函数中调用my_mutex_的unlock()进行解锁
}
}
bool PopMsg(int &cmd) {
{
std::lock_guard<std::mutex> lkgd(my_mutex_);// 构造函数中调用了my_mutex_的lock()成员函数
if (!my_list_.empty()) {
cmd = my_list_.front();
my_list_.erase(my_list_.begin(), my_list_.begin() + 1);
return true;
}
}// 执行完该程序块之后,调用lkgd的析构函数,在析构函数中调用my_mutex_的unlock()进行解锁
return false;
}
void InMsg(int n) {
for(int i = 0; i < n; i ++) {
// std::chrono::microseconds dura(1);
// std::this_thread::sleep_for(dura);
int cmd = 0;
if (PopMsg(cmd)) {
cout << "pop msg is:" << cmd << endl;
} else {
cout << "list is empty!!" << endl;
}
}
}
private:
vector<int> my_list_;
mutex my_mutex_;
};
int main() {
ThreadTest a;
int n = 100000;
thread t1(&ThreadTest::OutMsg, &a, n);
thread t2(&ThreadTest::InMsg, &a, n);
t1.join();
t2.join();
return 0;
}