什麼是協程
相信很多人看到 c++20 提供的新功能協程,因該滿頭問號,更不用說去使用了? 到底協程是不是多線程? 直接說答案:不是!
協程本身並不是一種多線程技術,而是一種在單線程內實現非阻塞異步操作的方法。 協程允許在一個線程內進行高效的任務切換,而不需要多線程帶來的上下文切換和同步開銷。 了解到這點後,再去看 code 就會清楚很多了。
協程與多線程的區別
協程(Coroutines):
- 單線程內的協作: 協程在同一線程內運行,通過讓出和恢復控制權來實現異步操作。
- 輕量級: 協程的上下文切換比線程輕量得多,因為不涉及操作系統層面的上下文切換。
- 控制權讓出: 協程可以在需要等待的時候讓出控制權,允許其他協程運行。
- 簡化異步編程: 協程可以使異步代碼看起來像同步代碼,簡化了異步編程的複雜性。
多線程(Multithreading):
- 多線程並行執行: 多線程允許在多個處理器核上並行運行代碼,適合計算密集型任務。
- 上下文切換開銷高: 線程的上下文切換涉及操作系統的調度,開銷較高。
- 需要同步機制: 多線程之間需要使用鎖或其他同步機制來保護共享數據,避免競態條件。
- 複雜性: 多線程編程通常更複雜,容易出現死鎖、競態條件等問題。
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
#include <iostream>
#include <coroutine>
#include <thread>
struct ReturnObject {
struct promise_type {
ReturnObject get_return_object() {
return {
.h = std::coroutine_handle<promise_type>::from_promise(*this)
};
}
std::suspend_never initial_suspend() {
return {};
}
std::suspend_never final_suspend() noexcept {
return {};
}
void unhandled_exception() {
std::terminate();
}
void return_void() {}
};
std::coroutine_handle<promise_type> h;
};
ReturnObject counter() {
std::cout << "enter counter: " << std::endl;
for (int i = 0; i < 3; ++i) {
std::cout << "counter: " << i << std::endl;
co_await std::suspend_always{};
std::cout << "thread ID:[" << std::this_thread::get_id() << "]" << std::endl;
std::cout << "counter after suspend_always: " << i << std::endl;
}
std::cout << "leave counter: " << std::endl;
}
int main() {
std::cout << "main thread ID:[" << std::this_thread::get_id() <<"]" << std::endl;
std::cout << "-----------" << std::endl;
auto c = counter();
for (int i = 0; i < 3; ++i) {
std::cout << "main loop i: " << i << std::endl;
c.h();
std::cout << "-----------" << std::endl;
}
return 0;
}
Note
1
2
3
4
5
ReturnObject get_return_object() {
return {
.h = std::coroutine_handle<promise_type>::from_promise(*this)
};
}
這邊的 .h 就是使用到 designated initializer
執行結果
- 可以看到從頭到尾都是 main thread 在執行
- 執行順序 counter’s loop -> main’s loop -> counter’s loop….
使用時機
協程適用於以下情境:
- 異步 I/O 操作: 如網絡請求、文件讀寫等,需要等待 I/O 操作完成而不阻塞主線程。
- 事件驅動的應用: 如 GUI 應用程序,需要在處理用戶事件時保持界面響應。
- 遊戲開發: 需要處理異步事件,如資源加載、網絡通信等。
- 協作多任務: 需要在單線程內高效地切換任務,如數據流處理、異步計算等。
如果你看到這裡還是覺得有點模糊,沒關係,第一次看都是這樣的, 只要記住最重要的一點,協程不是一種多線程技術,其餘的多看幾次就可以了解了。

