C++11實現的協程庫,支持Win,Linux,Mac。
Fork Form https://github.com/mfichman/coro/commit/2d597a7ebe08bc28d91b98c942be17eb224b8853
C++ 協程的消息還是挺少的,補充一份。
協同程序(coroutine)簡稱協程
協同程序
參考Lua中協同程序的介紹
http://www.runoob.com/lua/lua-coroutine.html
Coroutine 協程類
協程類,用於封裝函數。一個協程只能包含一個函數,在執行期間的任何時刻可暫停和恢復。每個協程分配CORO_STACK_SIZE(1M)的內存大小。
class Coroutine : public std::enable_shared_from_this<Coroutine> {
Coroutine(F func); // 構造函數,參數是一個函數,作爲綁定函數
void start() throw(); // 執行綁定函數
內部 func_(); // 執行綁定的函數
event_->notifyAll();
void exit(); //當協程完成執行時,此函數運行。
內部event_->notifyAll();
main()->swap();
void swap(); // 將運行權給其它協程,當前正在執行的協程會掛起。
void yield(); // 掛起coroutine,將coroutine設置爲掛起狀態。RUNNING狀態切換爲RUNNABLE狀態,下次循環會繼續運行
void block(); // 阻止當前協同程序,直到發生某些I/O事件。 協程會在明確安排之前不得重新安排。RUNNING狀態切換爲BLOCKED狀態
內部hub()->blocked_++;
void unblock(); // 解除阻止
內部hub()->blocked_--;
void wait(); // 阻止當前協同程序,直到發生某些事件。 協程不會被重新安排,直到明確安排。BLOCKED狀態切換爲RUNNABLE狀態
內部hub()->waiting_++;
void notify(); // 在事件發生時取消阻止協同程序。
內部hub()->waiting_--;
// 部分成員變量
Ptr<Event> event_; // 信號
}
Coroutine 狀態切換
這個狀態的過程比較的重要!
1、NEW --> RUNNING --> EXITED
2、RUNNING --> BLOCKED --> RUNNABLE --> RUNNING --> EXITED
3、WAITING --> RUNNABLE --> RUNNING --> EXITED
Coroutine::Coroutine(); // 初始化狀態爲RUNNING狀態
Coroutine::~Coroutine(): // 切換爲DELETED狀態
void Coroutine::wait(); // RUNNING切換爲WAITING狀態
void Coroutine::notify(); // WAITING切換爲RUNNABLE狀態
void Coroutine::swap(); // 當前RUNNABLE切換爲RUNNING;NEW切換爲RUNNING;BLOCKED切換爲RUNNING。最重要的函數,控制協程的中斷,切換。
void Coroutine::exit(); // RUNNING切換爲EXITED狀態
void Coroutine::yield(); // RUNNING切換爲RUNNABLE狀態
void Coroutine::block(); // RUNNING切換爲BLOCKED狀態
void Coroutine::unblock(); // BLOCKED切換爲RUNNABLE狀態
Coroutine 內存分配
每個協程分配CORO_STACK_SIZE(1M)的內存大小,保存到成員變量
Stack stack_;,所分配的內存會在Coroutine析構函數執行後釋放。
if defined(_WIN32)
struct StackFrame {
void* fs8; // 棧頂
void* fs4; // 棧低
void* fs0; // Root-level SEH handler
void* rdi;
void* rsi;
void* rdx;
void* rcx;
void* rbx;
void* rax;
void* rbp;
void* returnAddr; // coroStart() stack frame here
};
stackPointer_ // 保存初始化的信息地址,未被調用。
Coroutine析構的時候釋放內存。
Event 協程同步事件
協程的調用過程是單線程的,Event的控制是通過改變協程的狀態來控制的。
void notifyAll(); // 通知所有協程運行,添加到std::vector<EventRecord>容器中
void wait(); // 等待協程信號。實際是將協程的RUNNING切換爲WAITING狀態並掛起,調用其他的協程。
Hub 管理協程容器
Hub類管理所有coroutines,events和I/O。
等待的事件(通道,I/O等)發出完成信號,在執行調用。
被定義爲一個靜態變量,通過coro::run()調用。
void quiesce(); // 遍歷runnable_協程容器,更新協程的狀態(void Coroutine::swap()),將RUNNABLE狀態的協程保存到runnable_容器。
void poll(); // 輪詢I/O事件。
void Hub::run(); // 循環運行,每次取一個協程
常用API
// 添加函數到協程容器
coro::start(baz);
// 掛起,RUNNING狀態切換爲RUNNABLE狀態
coro::yield();
// 函數中調用,用於定時中斷,中斷時間到,繼續執行
coro::sleep(coro::Time::millisec(1000));
// 執行協程容器
coro::run();
Demo1 - Basic 基礎例子
#include "coro/Common.hpp"
#include "coro/Coroutine.hpp"
#include "coro/Hub.hpp"
void bar() {
for (auto i = 0; i < 2; ++i) {
coro::sleep(coro::Time::millisec(1000));
std::cout << "barrrrrr" << std::endl;
}
}
void baz() {
for (auto i = 0; i < 20; ++i) {
coro::sleep(coro::Time::millisec(100));
std::cout << "baz" << std::endl;
}
}
int main() {
auto cbaz = coro::start(baz);
auto cbar = coro::start(bar);
coro::run();
return 0;
}
Demo2 - Event 帶信號的例子
#include <coro/Common.hpp>
#include <coro/coro.hpp>
int main() {
auto event = coro::Event();
auto trigger = false;
auto notifier = coro::start([&]() {
printf("notified\n");
trigger = true;
event.notifyAll(); // 切換所有WAITING狀態的協程爲RUNNABLE狀態
});
auto waiter = coro::start([&]() {
printf("waiting\n");
event.wait([&]() { return trigger; }); // 等待信號
printf("done\n");
});
coro::run();
return 0;
}
Demo3 - Join 嵌套例子
#include <coro/Common.hpp>
#include <coro/coro.hpp>
int main() {
auto counter = 0;
auto one = coro::start([&]{
coro::yield(); // 掛起,RUNNING切換爲RUNNABLE狀態。恢復的時候繼續運行下去
assert(counter==0);
counter++;
std::cout << "one func" << counter << std::endl;
});
auto two = coro::start([&]{
one->join(); // two狀態有RUNNING改爲WAITING;one狀態由RUNNABLE改爲RUNNING。
assert(counter==1);
std::cout <<"two func" << counter << std::endl;
});
coro::run(); //
return 0;
}
Demo4 - Selector 信號和協程綁定
使用這個類,開發者可以決定是否執行協程函數。
Selector局部變量析構函數中會將協程的狀態由RUNNING切換爲WAITING。Selector中可以保持多個協程對象。
#include <coro/Common.hpp>
#include <coro/coro.hpp>
using namespace coro;
Ptr<Event> e1(new Event);
Ptr<Event> e2(new Event);
Ptr<Event> e3(new Event);
void publisher() {
e1->notifyAll(); // 通知執行
e2->notifyAll(); // 通知執行
}
void consumer() {
int count = 2;
while (count > 0) {
coro::Selector() // 綁定信號和協程對象
.on(e1, [&]() { count--; std::cout << "e1" << std::endl; })
.on(e2, [&]() { count--; std::cout << "e2" << std::endl; })
.on(e3, [&]() { std::cout << "e3" << std::endl; });
}
}
int main() {
auto b = coro::start(consumer);
auto a = coro::start(publisher);
coro::run();
return 0;
}