<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          叮咚 | 同步異步阻塞非阻塞

          共 4207字,需瀏覽 9分鐘

           ·

          2021-08-13 20:30

          低并發(fā)編程
          戰(zhàn)略上藐視技術(shù),戰(zhàn)術(shù)上重視技術(shù)

          自從我寫完《你管這破玩意叫 IO 多路復(fù)用》之后,我發(fā)現(xiàn)讀者問得最多的問題是,可不可以寫篇文章解釋下什么是同步異步阻塞非阻塞,老是傻傻分不清。
          我一開始也是非常困惑,尤其是看網(wǎng)上一些“生動(dòng)形象”的例子,比如燒水壺。
          但現(xiàn)在我感覺這個(gè)問題又沒什么好說的,不知道是不是我理解得有點(diǎn)膚淺,那我試著解釋一下。
          同步和異步,描述的是調(diào)用者,要不要主動(dòng)等待函數(shù)的返回值。
          這個(gè)就是同步
          public static void main() {
              int result = doSomeThing();
          }
          這個(gè)就是異步
          public static void main() {
              new Thread(() -> {
                  int result = doSomeThing();    
              })
          }
          當(dāng)然,異步可以配合回調(diào)機(jī)制,但這就和同步異步本身的區(qū)別沒啥關(guān)系了,添枝加葉的東西而已。
          再說阻塞和非阻塞,描述的是函數(shù)本身,在等待某一事件的結(jié)果時(shí),是將線程掛起,還是立即返回一個(gè)未就緒等信息。
          一般都是描述 IO 等,也別想其他的了,比如一個(gè)讀取磁盤數(shù)據(jù)的函數(shù)。
          這個(gè)就是阻塞
          public void int read(byte[] buffer) {
              while(磁盤未就緒) {
                  將當(dāng)前線程掛起并讓出 CPU;
              }
              // 此時(shí)磁盤已就緒
              真正去讀數(shù)據(jù)到 buffer 中
              return 讀到的字節(jié)數(shù);
          }
          這個(gè)就是非阻塞
          public void int read(byte[] buffer) {
              if(磁盤未就緒) {
                  // 立刻返回
                  return -1;
              }
              真正去讀數(shù)據(jù)到 buffer 中
              return 讀到的字節(jié)數(shù);
          }
          至于這個(gè)函數(shù)被調(diào)用者用同步還是異步的方式調(diào)用,都不影響這個(gè)函數(shù)本身是阻塞還是非阻塞的性質(zhì)。
          好了,我覺得到這里就解釋清楚了,真沒啥說的呀。
          至于特別多的人有困惑的地方,我總結(jié)出可能有三點(diǎn)。

          第一,分不清語境





          比如阻塞這個(gè)詞,用法太多了,你看下面這些句子。

          這個(gè)函數(shù)是阻塞的。

          這是個(gè)阻塞函數(shù)。

          這個(gè)方法調(diào)用的過程中因?yàn)橛?IO 事件被阻塞了。

          這個(gè)函數(shù)阻塞了主線程。

          這些句子要是真的糾結(jié)起來,那就壞了,總有你覺得怪怪的地方。
          因?yàn)橛眠@個(gè)詞的人,可能僅僅是表達(dá)出一個(gè),該線程因?yàn)槟承┦伦尦隽?CPU 資源暫時(shí)不往下走了的意思,即可。
          而且事實(shí)也是如此,沒人細(xì)摳這個(gè)詞究竟表示個(gè)啥意思。
          至于你還是糾結(jié)怎么辦呢?建議你看一看一個(gè)函數(shù)在最最最最底層,到底是怎么阻塞的,也就是怎么讓出 CPU 資源的,源碼長什么樣子。
          這里我也寫過一篇文章帶你看內(nèi)核源碼去解釋這個(gè)問題,叫《究竟什么是阻塞》
          我相信你看完之后,如果真的理解了,就再也不會(huì)糾結(jié)這些句子啥意思了,自己用阻塞這個(gè)詞也會(huì)隨性起來,你會(huì)覺得一頓花里胡哨解釋阻塞不阻塞的那些人好不可思議。

          第二,分不清層級(jí)





          比如 epoll 這個(gè)函數(shù),它是 IO 多路復(fù)用的一個(gè)系統(tǒng)調(diào)用函數(shù),好多人背誦 IO 模型八股文的時(shí)候都受過它的折磨。
          你會(huì)看到有的地方說,epoll 底層實(shí)現(xiàn) IO 事件響應(yīng)時(shí),是異步的,這也是同 select 和 poll 的一個(gè)區(qū)別。
          你又會(huì)看到有的地方說,epoll 是同步非阻塞 IO,因?yàn)槎嗦窂?fù)用在 IO 模型里就是站在同步非阻塞的地方嘛,那 epoll 也是多路復(fù)用那自然是同步的呀,剛剛怎么說是異步的呢。
          然后你又會(huì)看到,說 netty 是是一個(gè) IO 框架,是異步 IO 模型,可是 netty 底層用的就是 epoll 啊,那 epoll 也是異步的咯。
          我天,一會(huì)說異步,一會(huì)又說同步,一會(huì)又說異步,到底他喵的是啥啊?
          這就是層級(jí)問題了。
          先不拿同步異步說,這個(gè)第三點(diǎn)的時(shí)候再講,先拿阻塞和非阻塞說。
          一個(gè)函數(shù)是非阻塞的,那我用另一個(gè)函數(shù)把它包起來,對(duì)外提供一個(gè)阻塞的函數(shù)可不可以?
          當(dāng)然可以。
          // 這是個(gè)非阻塞的函數(shù)
          public void int read(byte[] buffer) {
              if(磁盤未就緒) {
                  // 立刻返回
                  return -1;
              }
              真正去讀數(shù)據(jù)到 buffer 中
              return 讀到的字節(jié)數(shù);
          }

          // 包一層,變成阻塞的
          public void int read2(byte[] buffer) {
              int result;
              while((result = read(buffer)) == -1) {
                  將線程掛起并讓出 CPU 資源
              }
              // 此時(shí)已讀到數(shù)據(jù)
              return result;
          }
          順便說一句,IO 多路復(fù)用里的 select 就是這么玩的,只不過人家是一組 IO 事件,這里只是一個(gè)。
          我再包一層新函數(shù),對(duì)外又提供了一個(gè)非阻塞的函數(shù),可不可以?
          當(dāng)然也可以,所以你看到說啥啥啥同步異步阻塞非阻塞時(shí),一定要知道人家在說哪一層。
          不談哪一層就開始和別人爭論這個(gè)東西是阻塞還是非阻塞,同步還是異步,基本都是在耍流氓。
          關(guān)于 epoll 的原理,我是理解不到太深,如果你有耐心,可以看飛哥帶你一行一行源碼讀 epoll 的文章,《深度揭秘 epoll 是如何實(shí)現(xiàn) IO 多路復(fù)用的》

          第三,隨意一點(diǎn)嘛





          有的時(shí)候,意思對(duì)了就行,你看有的人會(huì)說,select,poll,epoll 這些函數(shù)都是同步的,IO 有就緒的時(shí)候才會(huì)返回,沒有的時(shí)候會(huì)一直阻塞在那里直到有就緒的返回為止。
          那照我剛剛說的,同步異步是描述調(diào)用者是否主動(dòng)等待返回值,阻塞非阻塞才是描述函數(shù)本身要立即返回還是將線程掛起一會(huì)
          那就不對(duì)了呀,怎么能說 select 這些函數(shù)是同步的呢?應(yīng)該說他們是阻塞的呀。
          路走窄了呀兄弟,很多技術(shù)交流是沒那么在乎這些細(xì)節(jié)的,意思對(duì)了就行。
          而且 select 這種函數(shù)確實(shí)是阻塞的,而且調(diào)用方是要關(guān)心人家的返回值,并且在后面的邏輯中用到的,那只能用同步方式調(diào)用啊。
          當(dāng)然,關(guān)心返回結(jié)果,也可以異步調(diào)用,然后注冊一個(gè)回調(diào)函數(shù)來關(guān)心返回后的邏輯,但人家 select 沒提供回調(diào)函數(shù)注冊的功能啊。
          哦當(dāng)然,也可以通過像 Java 的 Future 這種方式異步獲取返回值,但沒必要啊。
          所以,當(dāng)有人說 select 是同步函數(shù),也沒啥毛病,表達(dá)的意思對(duì)了就行。

          ----- 華麗的分割線 ----

          千言萬語,總結(jié)成一句話就是,當(dāng)你底層的細(xì)節(jié)達(dá)到了源碼級(jí)的理解后,所有這些詞你將不再糾結(jié),也不再困惑,而且很多時(shí)候,知道意思對(duì)了就行,至于說阻塞非阻塞、同步異步,甚至是等待、掛起、讓出,這些詞也是表達(dá)的意思丟了就行,一切在源碼面前,都不再是秘密。
          建議多讀源碼,少看垃圾博客。
          所以其實(shí)如果你老是困惑這幾個(gè)詞的區(qū)別,其實(shí)你缺乏的可能是對(duì)底層的一些系統(tǒng)了解
          然后解決這個(gè)問題的唯一辦法就是花時(shí)間把底下的東西搞清楚,搞得模模糊糊的地方就去摳源碼,一點(diǎn)點(diǎn)摳就完事了,別怕耽誤時(shí)間。你耽誤的時(shí)間,在后面遇到問題的時(shí)候,都會(huì)給你找回來的。
          而且這個(gè)東西不能一口吃成個(gè)胖子,我以前就老想著一口吃個(gè)胖子,想盡快把一個(gè)大塊問題看懂,但反而耽誤時(shí)間,總是一遍遍從頭看起。
          后來就老實(shí)了,一點(diǎn)點(diǎn)看,看懂一點(diǎn)再看下一個(gè)點(diǎn),發(fā)現(xiàn)會(huì)越來越快的。
          同時(shí)這個(gè)公眾號(hào)也希望能一直保持,給大家講最本質(zhì)的東西,不浮于表面,我們一起努力吧!
          瀏覽 34
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美日韩黄色片在线 | 就是色欧美 | 免费播放一级A片 | 豆花视频在线观看一区二区三区 | 国产精品一二三级 |