c++介绍线程的屏障 八
c++ 20中引入了latch和barrier,提供了一种简单的同步机制,来协调多个线程
线程屏障提供了一个计数器,每个参与任务的线程完成自己的任务后,计数器减1.而需要同步的线程,屏障计数器可以阻塞,等待计数器为0时,继续执行后面的代码。
latch为一次性计数,当计数为0时就不能重复使用了。而barrirer为多个阶段的计数。
下面模拟一个比赛:发令枪响起,选手开跑,全部选手到达终点后统计成绩。
#include<array>
#include<vector>
#include<thread>
#include<iostream>
#include<mutex>
#include<algorithm>
#include<sstream>
#include<iomanip>
#include<semaphore>
#include<random>
#include<syncstream>
#include<latch>
using namespace std;
struct Runner
{
string name;
int time{ 0 };
void run(latch& start, latch& end)
{
//等待发令枪响
start.wait();
auto start_time = chrono::system_clock::now();
mt19937 eng{ random_device{}() };
uniform_int_distribution<unsigned int>uniform_int_distribution{ 0,2000 };
this_thread::sleep_for(chrono::microseconds(9600000 + uniform_int_distribution(eng)));
auto end_time = chrono::system_clock::now();
time = chrono::duration_cast<chrono::milliseconds>(end_time - start_time).count();
//跑完了
end.count_down(1);//计数器减1
}
bool operator<(const Runner& other)const { return time < other.time; }
};
int main()
{
vector<Runner> runners = { Runner{"张三"},Runner{"李四"},Runner{"王五"},Runner{"赵六"},Runner{"钱七"}};
const int runner_counter = runners.size();
latch start(1);
latch finish(runner_counter);
vector<thread>threads;
for (int i = 0; i < runner_counter; i++)
{
threads.emplace_back(&Runner::run,&runners[i],ref(start),ref(finish));
}
cout << "发令枪响起" << endl;
start.count_down();//阻塞等待计数器减为0
//等待所有选手跑完
finish.wait();//阻塞等待计数器减为0
cout << "比赛结果:\n=============================\n";
sort(runners.begin(), runners.end());
for (auto& runner : runners)
{
cout << runner.name << ":" << float(runner.time) / 1000 << "秒" << endl;
}
for (auto& t : threads)
{
t.join();
}
return 0;
}
打印结果
下面来看下barrier,与latch不同时,当barrier计数器为0时,开始新的一轮计时。
barrier是一个类模版,模版参数是一个可调用对象,每次计数器为0时会调用该函数。构造函数传递一个初始计数值。arrive将计数器减去传入的n.wait等待本阶段计数器到达为0. arrive_and_wait是arrive和wait一起使用,arrive_and_drop除了将本阶段计数减去n,还将下一阶段 初始计数值减去n.
下面模拟一个幸存者游戏,每轮淘汰不符合条件的玩家,一共进行五轮游戏。
#include<array>
#include<vector>
#include<thread>
#include<iostream>
#include<mutex>
#include<algorithm>
#include<sstream>
#include<iomanip>
#include<semaphore>
#include<syncstream>
#include<numeric>
#include<future>
#include<random>
#include<barrier>
using namespace std;
class Participant
{
public:
string name;
Participant(string name) :name(name) {}
template<typename BarrierType>
void run(BarrierType& finish)
{
mt19937 eng(random_device{}());
uniform_int_distribution<unsigned int>uniform_dist(1000, 2000);
for (int i = 0; i < 5; i++)
{
auto rnd = uniform_dist(eng);
this_thread::sleep_for(chrono::milliseconds(rnd));
alive = rnd % 3;
if (alive)
{
finish.arrive_and_wait();//阻塞等待计数器 减到0
}
else
{
finish.arrive_and_drop();//当前减1并将下一轮减1
break;
}
}
}
bool is_alive()const
{
return alive;
}
private:
bool alive = true;
};
int main()
{
vector<Participant>participant = { Participant{"张三"},Participant{"李四"},Participant{"王五"},Participant{"赵六"},Participant{"钱七"}};
const int participant_count = participant.size();
int round = 0;
auto on_complete = [&participant, &round]() noexcept {
round++;
cout << "第" << round << "轮幸存者:" << endl;
int count = 0;
for (auto& p : participant)
{
if (p.is_alive())
{
cout << p.name << " ";
count++;
}
}
cout << endl;
if (count == 0)
cout << "无" << endl;
};
barrier finish(participant_count,on_complete);//计数器减为0时回调on_complete函数
vector<thread>threads;
for (auto& p : participant)
{
threads.emplace_back([&p, &finish]()
{
p.run(finish);
});
}
for (auto& thread : threads)
thread.join();
return 0;
}