日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

tbox新增stackless协程支持

發布時間:2025/3/21 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 tbox新增stackless协程支持 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

為什么80%的碼農都做不了架構師?>>> ??

tbox之前提供的stackfull協程庫,雖然切換效率已經非常高了,但是由于每個協程都需要維護一個獨立的堆棧, 內存空間利用率不是很高,在并發量非常大的時候,內存使用量會相當大。

之前考慮過采用stacksegment方式進行內存優化,實現動態增漲,但是這樣對性能還是有一定的影響,暫時不去考慮了。

最近參考了下boost和protothreads的stackless協程實現,這種方式雖然易用性和靈活性上受到了很多限制,但是對切換效率和內存利用率的提升效果還是非常明顯的。。

因此,我在tbox里面也加上了對stackless協程的支持,在切換原語上參考了protothreads的實現,接口封裝上參考了boost的設計,使得更加可讀易用

先曬段實際的接口使用代碼:

tb_lo_coroutine_enter(coroutine) {while (1){tb_lo_coroutine_yield();} }

然后實測對比了下:

* 切換性能在macosx上比tbox的stackfull版本提升了5-6倍,1000w次切換只需要40ms * 每個協程的內存占用也減少到了只有固定幾十個bytes

那么既然stackless的效率提升這么明顯,stackfull模式還需要嗎?可以比較下兩者的優劣:

  • stackfull協程:易用性和靈活性非常高,但是內存使用過大
  • stackless協程:切換效率和內存利用率很高,更加輕量,但是使用上限制較多

由于stackless的實現比較輕量,占用資源也不是很多,因此tbox默認放置到了micro微內核模式下,作為基礎模塊,提供股嵌入式平臺使用

而一般情況下,如果對資源使用和切換性能要求不是非常苛刻的話,使用stackfull的方式會更加方便,代碼也更易于維護

具體如何選擇,可根據實際使用場景,自己選擇哦。。

切換

下面給的tbox的stackless協程切換實例,直觀感受下:

static tb_void_t switchtask(tb_lo_coroutine_ref_t coroutine, tb_cpointer_t priv) {// checktb_size_t* count = (tb_size_t*)priv;// enter coroutinetb_lo_coroutine_enter(coroutine){// loopwhile ((*count)--){// tracetb_trace_i("[coroutine: %p]: %lu", tb_lo_coroutine_self(), *count);// yieldtb_lo_coroutine_yield();}} } static tb_void_t test() {// init schedulertb_lo_scheduler_ref_t scheduler = tb_lo_scheduler_init();if (scheduler){// start coroutinestb_size_t counts[] = {10, 10};tb_lo_coroutine_start(scheduler, switchtask, &counts[0], tb_null);tb_lo_coroutine_start(scheduler, switchtask, &counts[1], tb_null);// run schedulertb_lo_scheduler_loop(scheduler);// exit schedulertb_lo_scheduler_exit(scheduler);} }

其實整體接口使用跟tbox的那套stackfull接口類似,并沒有多少區別,但是相比stackfull還是有些限制的:

1. 目前只能支持在根函數進行協程切換和等待,嵌套協程不支持 2. 協程內部局部變量使用受限

對于限制1,我正在研究中,看看有沒有好的實現方案,之前嘗試過支持下,后來發現需要按棧結構分級保存每個入口的label地址,這樣會占用更多內存,就放棄了。 對于限制2,由于stackless協程函數是需要重入的,因此目前只能在enter()塊外部定以一些狀態不變的變量,enter()塊內部不要使用局部變量

接口設計上,這邊采用boost的模式:

// enter coroutine tb_lo_coroutine_enter(coroutine) {// yieldtb_lo_coroutine_yield(); }

這樣比起protothreads的那種begin()和end(),更加可讀和精簡,接口也少了一個。。

參數傳遞

tb_lo_coroutine_start的最后兩個參數,專門用來傳遞關聯每個協程的私有數據priv和釋放接口free,例如:

typedef struct __tb_xxxx_priv_t {tb_size_t member;tb_size_t others;}tb_xxxx_priv_t;static tb_void_t tb_xxx_free(tb_cpointer_t priv) {if (priv) tb_free(priv); }static tb_void_t test() {tb_xxxx_priv_t* priv = tb_malloc0_type(tb_xxxx_priv_t);if (priv){priv->member = value;}tb_lo_coroutine_start(scheduler, switchtask, priv, tb_xxx_free); }

上述例子,為協程分配一個私有的數據結構,用于數據狀態的維護,解決不能操作局部變量的問題,但是這樣寫非常繁瑣

tbox里面提供了一些輔助接口,用來簡化這些流程:

typedef struct __tb_xxxx_priv_t {tb_size_t member;tb_size_t others;}tb_xxxx_priv_t;static tb_void_t test() {// start coroutine tb_lo_coroutine_start(scheduler, switchtask, tb_lo_coroutine_pass1(tb_xxxx_priv_t, member, value)); }

這個跟之前的代碼功能上是等價的,這里利用tb_lo_coroutine_pass1宏接口,自動處理了之前的那些設置流程, 用來快速關聯一個私有數據塊給新協程。

掛起和恢復

這個跟stackfull的接口用法上也是一樣的:

tb_lo_coroutine_enter(coroutine) {// 掛起當前協程tb_lo_coroutine_suspend(); }// 恢復指定協程(這個可以不在協程函數內部使用,其他地方也可以調用) tb_lo_coroutine_resume(coroutine);

掛起和恢復跟yield的區別就是,yield后的協程,之后還會被切換回來,但是被掛起的協程,除非調用resume()恢復它,否則永遠不會再被執行到。

等待

當然一般,我們不會直接使用suspend()和resume()接口,這兩個比較原始,如果需要定時等待,可以使用:

tb_lo_coroutine_enter(coroutine) {// 等待1stb_lo_coroutine_sleep(1000); }

來掛起當前協程1s,之后會自動恢復執行,如果要進行io等待,可以使用:

static tb_void_t tb_demo_lo_coroutine_client(tb_lo_coroutine_ref_t coroutine, tb_cpointer_t priv) {// checktb_demo_lo_client_ref_t client = (tb_demo_lo_client_ref_t)priv;tb_assert(client);// enter coroutinetb_lo_coroutine_enter(coroutine){// read dataclient->size = sizeof(client->data) - 1;while (client->read < client->size){// read itclient->real = tb_socket_recv(client->sock, (tb_byte_t*)client->data + client->read, client->size - client->read);// has data?if (client->real > 0) {client->read += client->real;client->wait = 0;}// no data? wait itelse if (!client->real && !client->wait){// 等待socket數據tb_lo_coroutine_waitio(client->sock, TB_SOCKET_EVENT_RECV, TB_DEMO_TIMEOUT);// 獲取等到的io事件client->wait = tb_lo_coroutine_events();tb_assert_and_check_break(client->wait >= 0);}// failed or end?else break;}// tracetb_trace_i("echo: %s", client->data);// exit sockettb_socket_exit(client->sock);} }

這個跟stackfull模式除了局部變量的區別,其他使用上幾乎一樣,也是同步模式,但是實際上tbox已經在底層把它放入了poller輪詢器中進行等待

在沒有數據,調用tb_lo_coroutine_waitio進行socket等待事件后,tbox會自動啟用stackless調度器內部的io調度器(默認是不啟用的,延遲加載,減少無畏的資源浪費)

然后進行poll切換調度(內部根據不同平臺使用epoll, kqueue, poll, 后續還會支持iocp)。

如果有事件到來,會將收到事件的所有協程恢復執行,當然也可以指定等待超時,超時返回或者強行kill中斷掉。

tbox中內置了一個stackless版本的http_server,實現也是非常輕量,經測試效率還是非常高的, 整體表現比stackfull的實現更好。

更多stackless接口使用demo,可以參考tbox的源碼

信號量和鎖

這個就簡單講講了,使用跟stackfull的類似,例如:

// the lock static tb_lo_lock_t g_lock;// enter coroutine tb_lo_coroutine_enter(coroutine) {// loopwhile (lock->count--){// enter locktb_lo_lock_enter(&g_lock);// tracetb_trace_i("[coroutine: %p]: enter", tb_lo_coroutine_self());// wait some timetb_lo_coroutine_sleep(1000);// tracetb_trace_i("[coroutine: %p]: leave", tb_lo_coroutine_self());// leave locktb_lo_lock_leave(&g_lock);} }// init lock tb_lo_lock_init(&g_lock);// start coroutine // ..// exit lock tb_lo_lock_exit(&g_lock);

這里只是舉個例子,實際使用中盡量還是別這么直接用全局變量哦。。


個人主頁:TBOOX開源工程

轉載于:https://my.oschina.net/tboox/blog/799860

總結

以上是生活随笔為你收集整理的tbox新增stackless协程支持的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。