0%

Coroutines in Visual C++ 2015 Update 1

Visual C++ 2015提供了一個Coroutine的實驗性實作,未來可能會被列入C++17當中,因此先來體驗一下

Generator

這寫法就跟Python差不多了,直接用yield就行了
不過跟Javascript的Generator還是有一定程度的不同

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
#include <iostream>
#include <experimental/generator>
using namespace std::experimental;
using namespace std;

generator<int> fib()
{
int a = 0;
int b = 1;
for (;;) {
yield a;
auto next = a + b;
a = b;
b = next;
}
}

int main(int argc, char* argv[])
{
for (auto v : fib()) {
if (v > 50)
break;
cout << v << endl;
}
}

async function

以下是個示範程式

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
#include <future>
#include <iostream>
using namespace std;
using namespace std::chrono;

// this could be some long running computation or I/O
future<int> calculate_the_answer()
{
return async([] {
this_thread::sleep_for(1s); return 42;
});
}

// Here is a resumable function
future<void> coro() {
cout << this_thread::get_id() << " Started waiting... \n";
auto result = await calculate_the_answer();
cout << this_thread::get_id() << " : woke up, get " << result << endl;
}

int main(int argc, char* argv[])
{
coro().get();
cout << this_thread::get_id() << ": back in main\n";
}

future<void>await的語法實在是有點突兀(尤其是其他語言都用asyncawait之後)
如果我們拿掉await會造成怎樣的結果

1
2
3
4
5
6
7
8
9
10
11
void noncoro() {
cout << this_thread::get_id() << " Started waiting... \n";
auto result = calculate_the_answer().get();
cout << this_thread::get_id() << " : woke up, get " << result << endl;
}

int main(int argc, char* argv[])
{
noncoro();
cout << this_thread::get_id() << ": back in main\n";
}

可以看到兩個函數的不同

  • coro: Main Thread Invoke calculate_the_answer -> Child Thread Return
  • noncoro: Main Thread Invoke calculate_the_answer -> Main Thread Return

Complex Example

這個範例有點複雜,有時間在研究吧

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
#include <windows.h>
#include <future>
#include <iostream>
#include <experimental/resumable>
using namespace std;
using namespace std::chrono;
using namespace std::experimental;

auto operator await(system_clock::duration duration) {
class awaiter {
static void CALLBACK TimerCallback(PTP_CALLBACK_INSTANCE, void *Context, PTP_TIMER) {
coroutine_handle<>::from_address(Context)();
}
PTP_TIMER timer = nullptr;
system_clock::duration duration;
public:
explicit awaiter(system_clock::duration d) : duration(d) {}
bool await_ready() const { return duration.count() <= 0; }
bool await_suspend(coroutine_handle<> resume_cb) {
int64_t relative_count = -duration.count();
timer = CreateThreadpoolTimer(TimerCallback, resume_cb.to_address(), nullptr);
SetThreadpoolTimer(timer, (PFILETIME)&relative_count, 0, 0);
return timer != 0;
}
void await_resume() {}
~awaiter() { if (timer) CloseThreadpoolTimer(timer); }
};
return awaiter{ duration };
}

future<void> test() {
cout << this_thread::get_id() << ": sleeping...\n";
await 1s;
cout << this_thread::get_id() << ": woke up\n";
}

int main() {
test().get();
cout << this_thread::get_id() << ": back in main\n";
}

Reference

Resumable functions in C++
Coroutines in Visual Studio 2015 - Update 1
Stackless coroutines with Visual Studio 2015