c++11多线程学习笔记(二)

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值