C++20實戰之channel
繼前面兩節的直播,講解了thread、jthread、stop_token、stop_source、stop_callback、cv、cv_any等的用法與底層實現,那么如何基于這些知識實現一個小項目呢?
于是引出了這篇,寫一個channel出來。
注:本節的代碼部分將會在星球公開,需要代碼的前往末尾獲取。
1.設計
1.1 接口設計
接口層面我們期望與通用的channel一樣,能夠一次性創建一個發送、接收的channel。
例如:
auto?[sender,?receiver]?=?make_channel<int>();
這里的實現可以通過類似make_unique提供一個對外的接口,返回值通過C++17的結構化綁定獲取,此時便得到了兩個channel。
最核心的兩個接口:發送與接收。
發送
sender.send(counter++);
接收
auto?value?=?receiver.recv();
除此之外,還可以在析構函數的時候提供一個shutdown接口,當send、recv阻塞的時候能夠被喚醒釋放。
例如:
bool?shutdown()?{if?(!state_)?{return?false;}light_city_lock?lk(state_->mutex);if?(source_.stop_requested())?{return?false;}source_.request_stop();state_->cv.notify_all();return?true;
}
1.2 內部隊列設計
內部隊列可以參考前面幾天發的如何設計一個線程安全的多生產者多消費者隊列如何實現一個線程安全多生產多消費者隊列?,這里可以內部維護一個state,然后帶上queue、mutex、cv_any。
struct?State?{std::queue<T>?queue;std::mutex?mutex;//?C++11?condition_variable_anystd::condition_variable_any?cv;
};
2.使用
使用起來就非常容易,只需要make出來兩個channel,然后去send與recv即可。
這里我使用了jthread + stop_token來完成這個工作,例如:
std::jthread?sender_thread([](Channel<int>?sender,?std::stop_token?token)?{int?counter?=?0;while?(!token.stop_requested())?{sender.send(counter++);std::this_thread::sleep_for(std::chrono::milliseconds(100));}},std::move(sender),?token);
接收端線程類似,在執行的差不多時,我們可以使用
stop_source.request_stop();
至此,便模擬了一個channel,發送端發送數據,接收端接收數據即可。
運行示例:
???channel?./a.out??????????????????
Received:?0
Received:?1
Received:?2
Received:?3
Received:?4
Received:?5
Received:?6
Received:?7
Received:?8
Received:?9