最近了解了一下 Eventloop 這個(gè)概念,所以想寫一篇文章整理一下思路。
1、Eventloop 是什么?
我在網(wǎng)上看了一些資料,但都比較復(fù)雜,而且大多和 JavaScript 扯上關(guān)系,對(duì)初學(xué)者不友好。
我個(gè)人理解的 Eventloop,其實(shí)就是在一個(gè)大循環(huán)里,處理各種各樣的事件。只是不同的 Eventloop 機(jī)制或者庫,在性能和適用場(chǎng)景之間有差別罷了。
一個(gè)程序,只要它需要一直工作,就會(huì)處于一個(gè)持續(xù)循環(huán)運(yùn)行的狀態(tài),我把這個(gè)循環(huán)的狀態(tài),稱為 Eventloop。
舉個(gè)簡(jiǎn)單的例子:
intmain() { intchoice; do{ //等待用戶輸入 choice=getch(); switch(choice){ case'q': break; case'a': add_record(); break; [...] } }while(choice!='q'); exit(EXIT_SUCCESS); }
上面這個(gè)程序,在一個(gè) while 循環(huán)里,根據(jù)不同的鍵盤輸入事件,而執(zhí)行不同的操作。
這就是一個(gè)簡(jiǎn)單的 Eventloop,只是這個(gè) Eventloop 只處理一種事件:鍵盤輸入,且是阻塞等待,雖然很簡(jiǎn)陋,但是對(duì)于上面這個(gè)場(chǎng)景而言,已經(jīng)夠用了。
Eventloop 隨處可見:
許多開源軟件,只要它們有持續(xù)運(yùn)行 + 事件處理的需求,就會(huì)有自己的 Eventloop 實(shí)現(xiàn),例如:
圖形庫 Qt 里的 QGuiApplication::run();
多媒體庫 SDL2 里的 SDL_PollEvent();
網(wǎng)絡(luò)庫 Mongoose 里的 mg_mgr_poll();
本質(zhì)上都是一種 Eventloop,只是由于需求和應(yīng)用場(chǎng)景的不一樣,各自的實(shí)現(xiàn)方法有所差異。
合格的 Eventloop:
一個(gè)合格的 Eventloop,需要有哪些特性?
我個(gè)人認(rèn)為:
1、不要阻塞,即不要調(diào)用可能會(huì)阻塞的系統(tǒng)調(diào)用,或總是以 nonblocking 的方式調(diào)用系統(tǒng)調(diào)用。
2、能異步處理事件。
3、性能盡量高,以滿足業(yè)務(wù)需求為下限。
滿足上述三點(diǎn),這個(gè) Eventloop 在功能上就是夠用的。
2、Eventloop 怎么用?
基于 select 的 Eventloop:
這是來自 UNIX 網(wǎng)絡(luò)編程 16.2 章節(jié)的一個(gè)例子,其大致代碼如下:
str_cli(FILE*fp,intsockfd) { ... //setnonblock val=Fcntl(sockfd,F_GETFL,0); Fcntl(sockfd,F_SETFL,val|O_NONBLOCK); //eventloop for(;;){ ... Select(maxfdp1,&rset,&wset,NULL,NULL); if(FD_ISSET(sockfd,&rset)){ //dosomething } ...//otherevent } }
這個(gè)程序會(huì)從標(biāo)準(zhǔn)輸入中讀取一行數(shù)據(jù),然后通過 socket 發(fā)送給服務(wù)端,然后接收服務(wù)端的響應(yīng),最后將響應(yīng)也寫到標(biāo)準(zhǔn)輸出。
最關(guān)鍵的點(diǎn)是先調(diào)用 fcntl 將所有的輸入輸出都設(shè)置為 nonblock,然后用 select 監(jiān)測(cè)所有的文件描述符。
基本上所有的開源事件庫,本質(zhì)上和這個(gè)程序沒差別。
各種開源的 Eventloop 庫:
由于 Eventloop 是一個(gè)比較通用的需求,在開源軟件里,有許多優(yōu)秀的異步事件庫都實(shí)現(xiàn)了這個(gè)功能。
比較適合嵌入式領(lǐng)域異步事件庫有 3 個(gè):
libevent:an event notification library.
名氣最大,應(yīng)用最廣泛,歷史最悠久的跨平臺(tái)事件庫。
libev:a high performance full-featured event loop written in C.
較 libevent 而言,設(shè)計(jì)更簡(jiǎn)練,但是對(duì) Windows 支持不夠好,并且和開源社區(qū)不怎么接軌。
libuv:a multi-platform support library with a focus on asynchronous I/O.
點(diǎn)擊查看大圖
開發(fā) node.js 的過程中需要一個(gè)跨平臺(tái)的事件庫,目前非?;钴S,推薦大家重點(diǎn)學(xué)習(xí)。
libev 最精簡(jiǎn),入門最容易,這里用它來介紹一下事件庫的用法:
#include#include //定義兩個(gè)事件watcher ev_iostdin_watcher; ev_timertimeout_watcher; //iowatcher的回調(diào)函數(shù) staticvoid stdin_cb(EV_P_ev_io*w,intrevents) { puts("stdinready"); ev_io_stop(EV_A_w); ev_break(EV_A_EVBREAK_ALL); } //timerwatcher的回調(diào)函數(shù) staticvoid timeout_cb(EV_P_ev_timer*w,intrevents) { puts("timeout"); ev_break(EV_A_EVBREAK_ONE); } intmain(void) { //定義eventloop structev_loop*loop=EV_DEFAULT; //初始化iowatcher ev_io_init(&stdin_watcher,stdin_cb,/*STDIN_FILENO*/0,EV_READ); ev_io_start(loop,&stdin_watcher); //初始化timerwatcher ev_timer_init(&timeout_watcher,timeout_cb,2,0.); ev_timer_start(loop,&timeout_watcher); //啟動(dòng)eventloop ev_run(loop,0); return0; }
libev 用 watcher 來檢測(cè)各種事件,當(dāng)事件發(fā)生時(shí),會(huì)調(diào)用 watcher 的回調(diào)函數(shù)。
那么,具體支持哪些事件類型呢?
如果你對(duì) libev 感興趣,可以根據(jù)下面這張圖閱讀其源碼:
審核編輯:湯梓紅
-
開源
+關(guān)注
關(guān)注
3文章
3368瀏覽量
42564 -
javascript
+關(guān)注
關(guān)注
0文章
519瀏覽量
53888
原文標(biāo)題:不懂就問:什么是Eventloop?
文章出處:【微信號(hào):gh_c472c2199c88,微信公眾號(hào):嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論