0%

c++协程初识

A coroutine is a function that maintains its state between calls. In that, it’s a bit like a function object, but the saving and restoring of its state between calls are implicit and complete.

占个位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include <coroutine>
#include <exception>
#include <format>
#include <iostream>
#include <random>
#include <thread>

static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_int_distribution<> dis(50, 200);

class CoroProducer {
public:
auto await_ready() {
std::format_to(std::ostreambuf_iterator<char>(std::cout),
"CoroProducer::await_ready\n");
return false;
}

auto await_suspend(std::coroutine_handle<> h) {
// do something before suspend
std::format_to(std::ostreambuf_iterator<char>(std::cout),
"CoroProducer::await_suspend: Try to read\n");
auto t = std::thread([h, this]() {
// simulate the async operation to get data
// std::this_thread::sleep_for(std::chrono::seconds(1));
auto sleep_ms = dis(gen) * 5;
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
this->_data = dis(gen);
std::format_to(std::ostreambuf_iterator<char>(std::cout),
"CoroProducer::await_suspend: Awake. data: {}\n", _data);
h.resume();
});

t.detach();
}

auto await_resume() {
// do something after resume
return _data;
}

private:
int _data{};
};

template <typename T> class CoroConsumer {
public:
CoroConsumer(T v) : _value(v) {}

auto await_ready() { return false; }

auto await_suspend(std::coroutine_handle<> h) {
std::format_to(std::ostreambuf_iterator<char>(std::cout),
"CoroConsumer: value: {}\n", _value);
h.resume();
}

auto await_resume() {
// do something after resume
}

private:
T _value;
};

template <typename T> struct CoroRet {
struct promise_type;

struct promise_type {
T _value;

auto initial_suspend() {
return std::suspend_never{};
// return std::suspend_always{};
}

auto return_value(T v) { _value = v; }

auto yield_value(T v) {
_value = v;
return CoroConsumer{v};
}

auto final_suspend() noexcept {
// return std::suspend_never{}; // auto destroy the handle
return std::suspend_always{}; // let the caller to destroy the handle
}

auto get_return_object() {
return CoroRet<T>{HandleType::from_promise(*this)};
}

auto unhandled_exception() { std::terminate(); }
};

using HandleType = std::coroutine_handle<promise_type>;

CoroRet(HandleType h) : _handle(h) {}

CoroRet(const CoroRet &) = delete;

CoroRet(CoroRet &&s) noexcept : _handle(s._handle) { s._handle = nullptr; }

~CoroRet() {
if (_handle) {
_handle.destroy();
}
}

CoroRet &operator=(const CoroRet &) = delete;

CoroRet &operator=(CoroRet &&s) noexcept {
_handle = s._handle;
s._handle = nullptr;
return *this;
}

auto get() { return _handle.promise()._value; }

auto done() const { return _handle.done(); }

private:
HandleType _handle;
};

CoroRet<int> add(int times) {
std::format_to(std::ostreambuf_iterator<char>(std::cout),
"Accumulate Start\n");
int total = 0;
for (int i = 0; i < times; i++) {
auto val = co_await CoroProducer{};
co_yield val;
total += val;
std::format_to(std::ostreambuf_iterator<char>(std::cout),
"times: {}, total: {}\n", i, total);
}

std::format_to(std::ostreambuf_iterator<char>(std::cout), "Accumulate End\n");

co_return total;
}

int main() {
auto res = add(5);
while (!res.done()) {
// busy waiting
}

std::format_to(std::ostreambuf_iterator<char>(std::cout), "Result: {}\n",
res.get());
return 0;
}