线程同步

  线程同步用于管理线程运行的顺序。当每个线程只运行一次时,线程同步用于管理各线程按顺序运行,称为有序同步。当线程内有循环体时,相当于一个线程运行多次,有无序同步和有序同步两种需求。无序同步:所有线程都执行完一次循环体再进行下一次循环,每次执行时线程的顺序任意。有序同步:所有线程按顺序都执行一次循环体再进行下一次循环。

1. 有序同步


  16 个线程按顺序输出一句话。重复 5 次这样的顺序输出。

1. 信号量

class Sync {
public:
	Sync(size_t x = 1) :thread_num(x), con(0) {}
	void lock(int tid) {
		unique_lock<mutex> locker(mx);
		cv.wait(locker, [&]() {return con == tid; });
	}
	void info() {
		if (con == thread_num - 1)
			cout << "-- 完成一轮。" << endl;
		con = (con + 1) % thread_num;
		// lock(con);
		cv.notify_all();
	}
private:
	size_t thread_num;
	condition_variable cv;
	mutex mx;
	int con;
};

  令条件 con 为 n 则线程 n 运行。
  第 11、13 行:调用 info 函数的线程已经执行完毕,在它执行时可能有很多线程去等待条件,第 11 行设置一个条件,第 13 行让满足条件的线程获得锁,不满足条件的线程也不再继续等待条件。第 12 行确保满足条件的线程在等待条件。

2. 线程函数

void kernel(const int tid, Sync& sync) {
	for (int i = 0; i < 5; i++) {
		sync.lock(tid);
		cout << "<" << tid << ">" << endl;
		sync.info();
	}
}

3. 测试

int main() {
	const int thread_num = 16;
	Sync sync(thread_num);
	vector<thread> pool;
	for (int tid = 0; tid < thread_num; tid++) {
		pool.push_back(thread(kernel, tid, ref(sync)));
		//Sleep(20);
	}
	for (int i = 0; i < thread_num; i++)
		pool[i].join();
	system("pause");
}

4. 头文件

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
using namespace std;

2. 无序同步


  16 个线程各输出一句话。重复 5 次这样的输出。
  应用场景:树状加法、读写操作。下面使用条件变量实现无序同步。

1. 信号量

class Sync {
public:
	Sync(size_t x = 1) :thread_num(x), wait_num(0) {
		mxs = new mutex[thread_num];
		con = new bool[thread_num];
		memset(con, 0, sizeof(bool) * thread_num);
	}
	void lock(int i) {
		unique_lock<mutex> locker(mxs[i]);
		cv.wait(locker, [&]() {return !con[i]; });
		con[i] = true;
	}
	void info() {
		wait_num++;
		if (wait_num == thread_num) {
			cout << "-- 完成一轮。" << endl;
			memset(con, 0, sizeof(bool) * thread_num);
			wait_num = 0;
			cv.notify_all();
		}
	}
	~Sync() {
		delete[] mxs;
		delete[] con;
		mxs = nullptr;
		con = nullptr;
	}
private:
	size_t thread_num;
	condition_variable cv;
	mutex* mxs;
	bool* con;
	atomic<int> wait_num;
};

  Sync 这个类的功能相当于信号量。条件变量和互斥锁、条件配合使用,所以 mxs 是各线程的互斥锁,con 是各互斥锁的条件。我们让每个线程有一个互斥锁,目的是控制它什么时候运行,什么时候等待。
  有互斥锁,为什么还使用条件变量:一个进程第一次申请锁没有释放,第二次再申请那个锁就会引起程序崩溃。而使用条件变量可以在申请锁前等待条件,避免直接申请引起崩溃,如第 10 行。如果条件满足,wait 函数会原子地 unlock 锁然后再 lock 锁。

2. 线程函数

void kernel(const int tid, Sync& sync) {
	for (int i = 0; i < 5; i++) {
		sync.lock(tid);
		cout << "<" << tid << ">" << endl;
		sync.info();
	}
}

3. 测试

int main() {
	const int thread_num = 16;
	Sync sync(thread_num);
	vector<thread> pool;
	for (int tid =0; tid < thread_num; tid++) {
		pool.push_back(thread(kernel, tid, ref(sync)));
		//Sleep(20);
	}
	for (int i = 0; i < thread_num; i++)
		pool[i].join();
	system("pause");
}

  thread 类的拷贝构造函数是删除的,所以要在 vector 的 push_back 中创建线程,而不能把创建的线程对象 push_back 到 vector。或者通过移动构造函数把线程对象 push_back 到 vector。

4. 头文件

#include <iostream>
#include <mutex>
#include <atomic>
#include <thread>
#include <vector>
using namespace std;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值