• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            JUST DO IT

            我之所以在這里,只是因?yàn)槲蚁胍谶@里

            高效異步IO的設(shè)計(jì)開發(fā)

            高效異步IO,按我理解起來應(yīng)該不光要達(dá)到程序運(yùn)行時(shí)的高效,當(dāng)然也要達(dá)到開發(fā)效率的高效,其中還包括很重要一點(diǎn)就是質(zhì)量,對(duì)于很多從未做過異步IO的人來說,初次嘗試異步IO肯定會(huì)碰到不少困難,因?yàn)椴还馐菍?duì)編程能力的考驗(yàn),也需要開發(fā)者對(duì)操作系統(tǒng)的IO操作有相當(dāng)?shù)牧私猓ㄋ臋C(jī)制和部分原理。異步IO中也有高效低效之分,但主要還是要看具體的應(yīng)用到底需要什么樣機(jī)制。比如大家熟知的select就是個(gè)非常通用且跨平臺(tái)的方法,由于select中需要把大量的時(shí)間花在維護(hù)IO句柄上,導(dǎo)致其效率大打折扣,一般來說,對(duì)于小并發(fā)的異步IO操作,比如普通的客戶端或者是小并發(fā)量的服務(wù)器,它的效率可能也足夠了。關(guān)于select的效率問題其實(shí)從各平臺(tái)上對(duì)于FD_SETSIZE的定義就能看出一些來,在windows平臺(tái)上,F(xiàn)D_SETSIZE是64,在Linux平臺(tái)上是1024,也就是說,對(duì)于平臺(tái)提供商來說也不指望他們提供的select能給你多大的并發(fā)吞吐能力,但由于select的簡(jiǎn)單和普及,其應(yīng)用面還是很廣,很多時(shí)候確實(shí)也不需要太多的并發(fā)量。其實(shí)說到高效異步IO的開發(fā),我們也說了,不光是考慮到程序運(yùn)行時(shí)的效率,還要考慮開發(fā)的效率和軟件的質(zhì)量,說到這里,其實(shí)select這么個(gè)簡(jiǎn)單的機(jī)制有時(shí)候用起來也不那么簡(jiǎn)單,而且還會(huì)出很多錯(cuò)誤。

            說到select重復(fù)的維護(hù)句柄的開銷,其實(shí)也是有解決方法的,好的解決方法效率會(huì)提高很多,但是重復(fù)工作還是要做的,比如當(dāng)select返回結(jié)果是0,或者當(dāng)能確定不需要增減IO句柄時(shí),我們可以簡(jiǎn)單的把原先保存的FD_SET副本重新寫入,這樣可以減少重新生成FD_SET的開銷,內(nèi)存復(fù)制效率顯然高于隊(duì)列的一次次遍歷,這是顯而易見的。當(dāng)然對(duì)于大并發(fā)量的IO操作來說,這種方法對(duì)于效率的提高也是很有限的,說到底即使采用異步IO效率也并不一定就高,還要取決于很多其它因素。其中很重要一點(diǎn)大家別忘記把偵聽端口設(shè)置成異步的,雖然不設(shè)置的話程序運(yùn)行后好像沒有什么不正常,但光從表面上就可以看到CPU占用率明顯偏高,當(dāng)然還會(huì)有一些其它問題產(chǎn)生,這個(gè)問題比較復(fù)雜,這里不作描述了。并發(fā)操作的效率還取決于“連接-->斷開->連接”的頻度,頻繁的“連接-->斷開->連接”也會(huì)產(chǎn)生不小的開銷,當(dāng)然這些開銷和之后要講的真正實(shí)現(xiàn)業(yè)務(wù)操作需要的開銷比起來其實(shí)要小得多,而且也是有很多方法可以規(guī)避的,對(duì)于采用不同的異步IO實(shí)現(xiàn)方法也是很不同的,效率差異會(huì)非常大。對(duì)我們來說,歸根結(jié)底是根據(jù)不同的模型采用不同的方法來提高效率,作為一個(gè)異步IO的前端“發(fā)生器”應(yīng)該盡可能避免在自己的工作上消耗過多的CPU資源,而是盡可能把CPU資源讓給具體的業(yè)務(wù)實(shí)現(xiàn)者。

            異步I/O中的Edge-Triggered和Level-Triggered是非常重要的概念;Edge-Triggered字面上理解就是指“邊界觸發(fā)”,說的是當(dāng)狀態(tài)變化的時(shí)候觸發(fā),以后如果狀態(tài)一直沒有變化或沒有重新要求系統(tǒng)給出通知,將不再通知應(yīng)用程序;Level-Triggered是指“狀態(tài)觸發(fā)”,說的是在某種狀態(tài)下觸發(fā),如果一直在這種狀態(tài)下就一直觸發(fā)。兩種觸發(fā)方式各有用途,應(yīng)根據(jù)不同的應(yīng)用采用不同的觸發(fā)方式。select一般默認(rèn)采用的是Level-Triggered,而EPoll既可以采用Edge-Triggered,也可以采用Level-Triggered,默認(rèn)是Level-Triggered,而MS的CPIO按這種定義來說應(yīng)該屬于Edge-Triggered。對(duì)于已經(jīng)封裝好的異步I/O架構(gòu)來說,具體采用哪種方式其實(shí)無傷大雅,因?yàn)闊o論采用哪種方式,都需要在內(nèi)部都實(shí)現(xiàn)正確了,并且讓使用者不再關(guān)心這種具體的觸發(fā)方式為好。
            void EPollReactor::NotifyMeWrite(SOCKET handle, SvcHandler *handler)
            {
                DIAMON_ASSERT(handle != INVALID_SOCKET);
                EPoll_Mod_Handle_Events(handle, EPOLLOUT/* | EPOLLET*/);
            }
            上述代碼中,就是在每次寫完數(shù)據(jù)后需要異步I/O框架再通知應(yīng)用程序關(guān)于寫完,其中EPoll_Mod_Handle_Events函數(shù)告訴系統(tǒng)給handle注冊(cè)上EPOLLOUT消息,這樣當(dāng)handle完成寫操作后,系統(tǒng)將通知框架寫完消息,是否加上EPOLLET完全取決于框架同應(yīng)用者之間的協(xié)議,其實(shí)本質(zhì)上就是框架對(duì)外提供的接口和調(diào)用約定。在Diamon::ACE中,采用的是Level-Triggered方式。
            void IOCPReactor::NotifyMeWrite(SOCKET handle, SvcHandler *handler)
            {
                DIAMON_ASSERT(handle != INVALID_SOCKET);
                IOCPSvcHandler *iocphandler = (IOCPSvcHandler *)handler;
                iocphandler->event_ &= (~IOCP_EVENT_READ);
                iocphandler->event_ |= IOCP_EVENT_WRITE;
            }
            對(duì)比一下,CPIO中的NotifyMeWrite做的不是通知系統(tǒng),而是告訴異步I/O框架自己接下來應(yīng)該處理寫事件了,而對(duì)系統(tǒng)的觸發(fā)工作完全是交給WSAWrite...之類的函數(shù)來完成的。想想啊,每次調(diào)用WSAWrite...不正是對(duì)系統(tǒng)說,我給這個(gè)handle注冊(cè)一下寫事件啊,下次還通知我,你可以試試不調(diào)用WSAWrite...了,下次肯定收不到寫通知了。

            同步問題是高效異步IO設(shè)計(jì)中非常重要但又常被忽視的問題(更不能濫用),不好的同步方法有時(shí)會(huì)限制應(yīng)用層的使用。我曾經(jīng)犯過這么一個(gè)錯(cuò)誤:select操作和FD_SET的操作是必須順序進(jìn)行的,否則會(huì)產(chǎn)生不可預(yù)期的后果。我們知道,在寫數(shù)據(jù)操作后往往需要將寫通知告訴應(yīng)用層,因此需要在寫操作后往寫FD_SET中設(shè)置handle,但是寫方法的調(diào)用和select方法的調(diào)用可能是在2個(gè)線程中,當(dāng)我一開始遇到這個(gè)問題的時(shí)候,我只簡(jiǎn)單的在select的外圍加了一對(duì)mutex操作,當(dāng)應(yīng)用層在收到讀通知中直接調(diào)用寫方法沒有問題,因?yàn)檫@時(shí)寫方法的調(diào)用和select調(diào)用處在一個(gè)線程中(肯定是順序執(zhí)行的),表面上看這個(gè)問題確實(shí)是解決了,但實(shí)際上卻隱藏了一些很嚴(yán)重的問題,當(dāng)寫方法是在其他的“業(yè)務(wù)處理器”中,比如另一個(gè)線程中,那么這種調(diào)用就會(huì)導(dǎo)致FD_SET的操作和select操作有同時(shí)進(jìn)行的可能,而當(dāng)這種情況發(fā)生后,顯而易見程序是不可能正常運(yùn)行了。而對(duì)于應(yīng)用程序來說,唯一解決這個(gè)問題的方法,就是要知道在select的外圍的mutex對(duì)象,然后在自己調(diào)用寫方法的外圍再包上一對(duì)這個(gè)mutex操作,雖然解決了FD_SET操作和select操作同步的問題,但實(shí)際上卻把問題更復(fù)雜化了,比如:一、讓應(yīng)用程序知道本不需要,更不應(yīng)該讓它知道的邏輯,導(dǎo)致了應(yīng)用層開發(fā)的復(fù)雜性,甚至給應(yīng)用程序帶來由于錯(cuò)誤使用導(dǎo)致更嚴(yán)重的問題;二、始終會(huì)存在某幾種情況會(huì)導(dǎo)致互鎖問題的產(chǎn)生。第二個(gè)問題可能會(huì)有點(diǎn)復(fù)雜,為了便于理解,我給大家解釋一下互鎖,我們考慮這么一種情況,有兩個(gè)任務(wù)分別使用mutexA和mutexB,任務(wù)1中使用的順序是...,mutexA.Lock(),...mutexB(),...,任務(wù)2中使用的順序是...,mutexB.Lock(),...mutexA(),...,當(dāng)任務(wù)1占用mutexA后等待mutexB前,任務(wù)2也剛好占用了mutexB在等待mutexA,這時(shí)候很明顯就制造了一個(gè)互鎖(交叉鎖)。第二個(gè)問題其實(shí)就是由這種調(diào)用下導(dǎo)致的某種互鎖情況??傊业倪@種不動(dòng)腦子的解決問題的方法導(dǎo)致了嚴(yán)重的應(yīng)用層問題。

            轉(zhuǎn)自: http://hippoweilin.mobile.spaces.live.com/arc.aspx

            posted on 2009-07-25 11:18 xmoss 閱讀(1544) 評(píng)論(0)  編輯 收藏 引用 所屬分類: 網(wǎng)絡(luò)相關(guān)

            久久ZYZ资源站无码中文动漫| 无码8090精品久久一区| www性久久久com| 久久se精品一区二区| 国产99久久久国产精免费| 亚洲精品无码久久久久AV麻豆| 久久婷婷五月综合成人D啪| 久久精品国产亚洲AV高清热| 久久精品国产99久久香蕉| 成人久久免费网站| 狠狠久久综合伊人不卡| 久久久久久久波多野结衣高潮| 久久国产一区二区| 久久久久波多野结衣高潮| 久久99国产精品二区不卡| 亚洲国产精品高清久久久| 久久精品国产色蜜蜜麻豆| 久久永久免费人妻精品下载| 欧美日韩精品久久久久 | 久久精品免费观看| 精品一二三区久久aaa片| 久久人人爽人人澡人人高潮AV| 九九99精品久久久久久| 久久久久久国产精品无码超碰| 亚洲精品无码久久久| 久久人人爽人人澡人人高潮AV| 久久66热人妻偷产精品9| 无码人妻精品一区二区三区久久 | 亚洲国产精品久久久久久| 久久精品国产精品亚洲精品| 欧美日韩成人精品久久久免费看| 久久99毛片免费观看不卡| 人妻无码久久一区二区三区免费| 国产香蕉久久精品综合网| 久久成人精品| 色综合合久久天天给综看| 久久久久久亚洲精品不卡| 久久免费线看线看| 99久久婷婷国产综合精品草原| 中文精品久久久久国产网址| 久久香蕉一级毛片|