epoll 中有兩種觸發模式,LT (水平觸發) 和 ET(邊緣觸發),網上關於這兩種的介紹很多,在這里不多贅述,只簡單說下這兩種模式下使用 阻塞/非阻塞 IO 的情況,以及對於 “為什么 ET 模式必須使用非阻塞 IO ?” 這個問題的看法。
個人認為使用 阻塞IO 潛在的問題在於,使用 阻塞 IO 去讀的時候,會導致在沒有數據可讀的時候,導致當前工作線程阻塞不工作。而 ET 模式與 LT 模式都是在有數據的情況下觸發,只不過觸發的時機不同。假定讀緩沖區 50b,而收到的包為 100b,有如下情況:
- 阻塞 IO
LT 模式下,由於只要有數據就會觸發讀,因此不會有問題,但是在 ET 模式下,由於在新的數據到來之前,都不會觸發讀事件,因此會導致剩下的 50b 沒有讀取到,所以為了保證能夠讀取到完整的包,需要使用 while(1) 之類的循環去讀,這就會導致在數據讀完之后,最后一次 read 阻塞,因為所有的數據都已經讀完了。
- 非阻塞 IO
在 LT 模式下,使用非阻塞 IO 的效果與阻塞 IO 差不多,在 ET 模式下,處理的邏輯與上面類似,但是由於使用的 非阻塞 IO ,因此不會導致最后一次 read 阻塞,而是會返回 EAGAIN 。
最后對於 “為什么 ET 模式必須使用非阻塞 IO ?” 這個問題。我的看法是應該將 “必須” 改成 “建議”,因為如果使用 阻塞IO ,也是有辦法規避上面的問題的,比如先獲取包體的大小之類的,但是這樣也會提高復雜度,效率也會更低下。對於監聽的 socket,最好使用 LT 模式,ET 模式會導致高並發情況下,有的客戶端會連接不上,除非使用 while 循環 accpet,且為非阻塞 socket 。對於讀寫的 socket,LT 模式下,阻塞和非阻塞效果都一樣。ET 模式下,建議使用非阻塞 IO,並一次性地完整讀寫全部數據。