屏障std::barrier
#include <mutex>
#include <iostream>
#include <future>
#include <barrier>
std::mutex m;
const int cnt = 3;
std::barrier b(cnt); // 创建一个barrier,需要3个线程到达同步点
double totalScore = 0;
void CalcExamScore(int i) {
m.lock();
std::cout << "读取第" << i << "个学生的成绩,并累加到总成绩\n";
m.unlock();
double cur = i * 10;
totalScore += cur;
b.arrive_and_wait(); // 到达同步点并等待
m.lock();
std::cout << "第"<< i<< "学生的成绩:" << cur << " 班级平均成绩" << totalScore/cnt << "\n";
m.unlock();
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < cnt; ++i) {
threads.emplace_back([i]() {CalcExamScore(i);});
}
for (auto& t : threads) {
t.join();
}
return 0;
}
回调函数
std::barrier 的回调函数先于所有线程从 arrive_and_wait() 返回执行。回调执行完毕后,屏障重置,各线程才继续往下走。这个顺序是标准保证的。回调函数在某个arrive_and_wait所在的线程执行。
以下程序分五步:
一,各线程读取分数。
二,回调函数执行 计算排名。
三, 各线程输入志愿。
四,根据分数和志愿匹配学校。
五,查看录取结果。
std::mutex m;
const int cnt = 3;
int trun = 0;
auto on_phase_complete = []() noexcept {
if (0 == trun)
{
std::cout << "计算排名\n";
}
else {
std::cout << "根据志愿匹配学校\n";
}
++trun;
};
std::barrier b(cnt, on_phase_complete); // 创建一个barrier,需要3个线程到达同步点
double totalScore = 0;
void CalcExamScore(int i) {
m.lock();
std::cout << "读取第" << i << "个学生的成绩,并累加到总成绩\n";
m.unlock();
double cur = i * 10;
totalScore += cur;
b.arrive_and_wait(); // 到达同步点并等待
m.lock();
std::cout << "第"<< i<< "个学生的成绩:" << cur << " 班级平均成绩" << totalScore/cnt << "。输入自己志愿\n";
m.unlock();
b.arrive_and_wait(); // 到达同步点并等待
m.lock();
std::cout << "第" << i << "个学生被某院校录取\n";
m.unlock();
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < cnt; ++i) {
threads.emplace_back([i]() {CalcExamScore(i);});
}
for (auto& t : threads) {
t.join();
}
return 0;
}
门闩std::latch
门栓只能使用一次,多次后异常。
上面代码的屏障改成std::latch b(cnt)后,就异常;
协程
线程切换:需要陷入内核态,保存全部通用寄存器、更新页表、修改内核调度队列,资源开销大,频繁切换会严重损耗性能;
C++20 协程切换:纯用户态操作,仅保存少量寄存器、局部变量、协程帧、Promise、协程句柄等轻量数据,挂起 / 恢复成本极低。
核心结论:C++20 是编译器原生无栈协程,轻量、切换成本低,但协程内部禁止阻塞。
只要函数内部包含co_await、co_yield、co_return三个关键字任意一个,这个函数就属于协程函数:
co_await:核心关键字,用于主动暂停协程,等待异步操作完成后恢复;
co_yield:暂停协程,同时向外产出一个返回值,典型用于生成器;
co_return:终止协程生命周期,返回最终结果
如果是C++23则代码很简洁。
// #include <generator> C++23支持的generator
#include <iostream>
#include <coroutine>
#include <exception>
#include <utility>
#include <cstddef>
//Generator 模拟C++23的std::generator
template <typename T>
class Generator {
public:
struct promise_type {
T current_value;
Generator get_return_object() {
return Generator{ std::coroutine_handle<promise_type>::from_promise(*this) };
}
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
std::suspend_always yield_value(T value) {
current_value = std::move(value);
return {};
}
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
class iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
iterator() : handle_(nullptr) {}
explicit iterator(std::coroutine_handle<promise_type> h) : handle_(h) {}
iterator& operator++() {
handle_.resume();
if (handle_.done()) handle_ = nullptr;
return *this;
}
void operator++(int) { ++(*this); }
bool operator==(const iterator& other) const { return handle_ == other.handle_; }
bool operator!=(const iterator& other) const { return !(*this == other); }
T& operator*() { return handle_.promise().current_value; }
T* operator->() { return &handle_.promise().current_value; }
private:
std::coroutine_handle<promise_type> handle_;
};
iterator begin() {
if (!handle_) return iterator{};
handle_.resume();
if (handle_.done()) return iterator{};
return iterator{ handle_ };
}
iterator end() { return iterator{}; }
Generator(Generator&& other) noexcept : handle_(std::exchange(other.handle_, nullptr)) {}
~Generator() { if (handle_) handle_.destroy(); }
Generator(const Generator&) = delete;
Generator& operator=(const Generator&) = delete;
private:
Generator(std::coroutine_handle<promise_type> h) : handle_(h) {}
std::coroutine_handle<promise_type> handle_;
};
Generator<long long> fibonacci(int count) {
long long a = 0, b = 1;
for (int i = 0; i < count; ++i) {
co_yield a;
auto next = a + b;
a = b;
b = next;
}
}
int main() {
for (auto num : fibonacci(10)) {
std::cout << num << " "; // Output: 0 1 1 2 3 5 8 13 21 34
}
}
co_yield a; 执行完后,就执行std::cout << num。
多次调用
同一个函数执行结束前,可以执行另外一个同名协程函数,互不影响。通过返回值区分。
int main() {
auto ret1 = fibonacci(3);
auto ret2 = fibonacci(3);
auto it1 = ret1.begin();
auto it2 = ret2.begin();
std::cout << *it1 << " " << *it2 << "\n";
++it1, ++it2;
std::cout << *it1 << " " << *it2 << "\n";
++it1, ++it2;
std::cout << *it1 << " " << *it2 << "\n";
}
0 0
1 1
1 1
连接服务端、发送消息、接受信息
协程分四步:一,异步连接服务端。二,异步发送消息。三,挂起线程。 四,同步接受消息并处理。协程两次执行完,第一次执行一二三,第二次执行四。
先将100个协程执行一次,执行完毕后,这些线程再执行一次。
由于网络回应时间占处理时间的绝大部分,故其它时间可忽略。其它时间忽略后:
使用协程,处理时间等于所有网站反应时间的最大值。
不使用协程,处理时间等于所有网站反应时间之和。
扩展阅读
| 计算几何为骨,排样优化为魂 |
|---|
| 作品一:亲士套料工具箱 作品二: 亲士CAD工具箱 |
| 经典文章推荐:二维排样 |
| 万物皆数学 |
| 查阅鄙人的博文,请点击博文下载学院导航 |
| 活到老,学到老。明朝中后期,大约50%的进士能当上堂官(副部及更高);能当上堂官的举人只有十余人。 |
| 子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/he_zhidan/article/details/167802433



