• <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>

            桃源谷

            心靈的旅行

            人生就是一場旅行,不在乎旅行的目的地,在乎的是沿途的風景和看風景的心情 !
            posts - 32, comments - 42, trackbacks - 0, articles - 0
              C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

            準則5:盡可能避免線程的延遲撤銷處理

            Posted on 2008-12-25 14:08 lymons 閱讀(1330) 評論(0)  編輯 收藏 引用 所屬分類: C++CUnix/Linux文章翻譯
            From 2008精選

            [] UNIX上C++程序設計守則(5)Add star


            準則5: 盡可能避免線程中做延遲撤銷的處理

            • 線程的異步撤消是指:一個線程發出中斷其他線程的處理的一個動作
            • 延遲撤消因為是規格自由度比較高、所以根據OS和C庫函數的版本它也有各式各樣的動作
              • 要想在不同的環境下都能穩定的動作的話,就必須要詳細調查運行環境和,對C庫函數進行抽象化,做必要的條件編譯
              • 在C++中、「撤消發生時的對象釋放」的實現不具有可移植性
            • 線程撤銷要慎重使用。在C++里不要使用

            說明:


            在前面我們已經講過,線程的撤消分為「異步」「延遲」這兩種類型、并且「異步撤消」也是非常容易引起各種復雜問題的元兇。


            那么,現在要在程序中除掉「延遲撤消」。延遲撤消雖然不會像異步撤消那樣會引起各種各樣的問題、但是、注意事項還是有很多的。只有把下面的這些注意事項全部都把握之后才能放心使用。


            注意事項1: 要好好把握撤消點


            和異步撤消不一樣的是、撤消處理一直會被延遲到在代碼上明示出來的撤消點之后才會被執行。如果編寫了一個具有延遲撤消可能的代碼、代碼中的那條語句是撤消點、必須要正確的把握。


            首先、調用過pthread_testcancel函數的地方就變成撤消點了。當然這個函數是、僅僅為了「變成延遲撤消」的目的而設置出來的函數。除此之外、某些標準庫函數被調用后會不會變成撤消點是在規格(SUSv3)中決定的。請參照規格說明、有下面的函數一覽。


            下面的函數撤消點

            accept, aio_suspend, clock_nanosleep, close, connect, creat, fcntl, fdatasync,
            fsync, getmsg, getpmsg, lockf, mq_receive, mq_send, mq_timedreceive,
            mq_timedsend, msgrcv, msgsnd, msync, nanosleep, open, pause, poll, pread,
            pselect, pthread_cond_timedwait, pthread_cond_wait, pthread_join,
            pthread_testcancel, putmsg, putpmsg, pwrite, read, readv, recv, recvfrom,
            (略)
            下面的函數不是撤消點

            access, asctime, asctime_r, catclose, catgets, catopen, closedir, closelog,
            ctermid, ctime, ctime_r, dbm_close, dbm_delete, dbm_fetch, dbm_nextkey, dbm_open,
            dbm_store, dlclose, dlopen, endgrent, endhostent, endnetent, endprotoent,
            endpwent, endservent, endutxent, fclose, fcntl, fflush, fgetc, fgetpos, fgets,
            fgetwc, fgetws, fmtmsg, fopen, fpathconf, fprintf, fputc, fputs, fputwc, fputws,
            (略)

            看到這些我想已經明白了、但是在規格中也說明了「能否成為撤消點跟具體的實現相關的函數」也是多數存在的。原因是、為了可移植性、保證「在一定的時間內讓線程的延遲撤消完成」是很困難的事情*1。做的不好的話、只要稍微一提升OS的版本就可能讓做出來的程序產品不能動作。


            即使是這樣那還想要使用延遲撤消嗎?


            注意事項2: 實現要知道cleanup函數的必要性


            可能被延遲撤銷的線程在運行的過程中,要申請資源的場合,一定要考慮到以下的幾點,否則就會編制出含有資源丟失和死鎖的軟件產品。


            例如編寫的下面的函數就不能被安全的延遲撤銷掉。

            void* cancel_unsafe(void*) {
            static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
            pthread_mutex_lock(&mutex); // 此處不是撤消點
            struct timespec ts = {3, 0}; nanosleep(&ts, 0); // 經常是撤消點
            pthread_mutex_unlock(&mutex); // 此處不是撤消點
            return 0;
            }
            int main(void) {
            pthread_t t;
            // pthread_create后馬發上收到一個有效的延遲撤消的要求
            pthread_create(&t, 0, cancel_unsafe, 0);
            pthread_cancel(t);
            pthread_join(t, 0);
            cancel_unsafe(0); // 發生死鎖!
            return 0;
            }

            在上面的樣例代碼中、nanosleep執行的過程中經常會觸發延遲撤銷的最終動作,但是這個時候的mutex鎖還處于被鎖定的狀態。而且、線程一被延遲撤消的話就意味著沒有人去釋放掉這個互斥鎖了*2。因此、在下面的main函數中調用同樣的cancel_unsafe函數時就會引起死鎖了。


            為了回避這個問題、利用pthread_cleanup_push函數在撤消時釋放掉互斥鎖的話就OK了,也就不會死鎖了。

            // 新增清除函數
            void cleanup(void* mutex) {
            pthread_mutex_unlock((pthread_mutex_t*)mutex);
            }

            // 粗體字部分是新增的語句
            void* cancel_unsafe(void*) {
            static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
            pthread_cleanup_push(cleanup, &mutex);
            pthread_mutex_lock(&mutex);
            struct timespec ts = {3, 0}; nanosleep(&ts, 0);
            pthread_mutex_unlock(&mutex);
            pthread_cleanup_pop(0);
            return 0;
            }

            注意事項3: 實現要清楚延遲撤消和C++之間的兼容度


            使用C語言的場合,利用上面的pthread_cleanup_push/pop函數就能安全地執行延遲撤消的動作,但是在C++語言的場合就會出現其他的問題。C++與延遲撤消之間的兼容度是非常差的。具體的表現有以下兩個問題:


            1. 執行延遲撤消的時候,內存棧上的對象的析構函數會不會被調用跟具體的開發環境有關系
              • GCC3版本就不會調用。
              • Solaris和Tru64 UNIX下的原生編譯器的場合,就調用析構函數(好像)
            2. pthread_cleanup_push/pop函數和C++的異常處理機制之間有著怎樣的相互影響也能具體環境有關

            不調用析構函數,或者在拋出異常的時候不能做cleanup處理,經常是發生內存泄漏,資源丟失,程序崩潰,死鎖等現象的原因。令人意外的是對于這個深層次的問題,就連Boost C++庫都束手無策。

            [Q] Why isn't thread cancellation or termination provided?

            [A] There's a valid need for thread termination, so at some point Boost.Threads probably will include it, but only after we can find a truly safe (and portable) mechanism for this concept.

            先必須確保對象的自由存儲,而后全都讓cleanup函數去釋放對象的方法也有,但是這次是犧牲了異常安全性。
            (原文沒有看明白:オブジェクトを必ずフリーストア上に確保し、解體を全て、クリーンナップハンドラに行わせる手もありますが、今度は例外安全性が犠牲になるでしょう。)


            應該說的是,在使用C++的工程里不對線程進行延遲撤消處理還是比較實際的。

            *1:好的問題是 gethostbyname()函數

            *2:異步撤消跟malloc函數的例子很相似

            原文地址:http://d.hatena.ne.jp/yupo5656/20040725/p2

            我的個人簡歷第一頁 我的個人簡歷第二頁
            国产精品久久午夜夜伦鲁鲁| 国产高潮国产高潮久久久91 | 久久无码中文字幕东京热| 亚洲国产成人久久一区WWW| 久久精品国产免费观看三人同眠| 亚洲va久久久噜噜噜久久男同| 91精品国产乱码久久久久久| 国产成人久久精品麻豆一区 | 久久午夜无码鲁丝片| 国产99久久九九精品无码| 久久午夜夜伦鲁鲁片免费无码影视 | 久久亚洲AV无码西西人体| 日韩乱码人妻无码中文字幕久久| 伊人久久综在合线亚洲2019| 久久人人爽人人爽人人片AV东京热| 人妻少妇久久中文字幕一区二区| 伊人久久免费视频| 久久精品国产亚洲av影院| 亚洲国产婷婷香蕉久久久久久| 久久精品国产91久久综合麻豆自制 | 久久久久久午夜精品| 国产亚州精品女人久久久久久| 777午夜精品久久av蜜臀| 少妇被又大又粗又爽毛片久久黑人| 久久天堂AV综合合色蜜桃网| 久久国产亚洲精品| 久久久无码精品午夜| 久久精品夜色噜噜亚洲A∨| 国内精品久久久久久野外| 久久精品国产亚洲AV麻豆网站| 亚洲七七久久精品中文国产| 午夜精品久久久久9999高清| 国产激情久久久久影院小草 | 久久夜色精品国产噜噜麻豆 | 欧美亚洲另类久久综合婷婷| 伊人丁香狠狠色综合久久| 91精品国产91久久久久久青草| 久久精品国产69国产精品亚洲| 久久99国产综合精品| 久久99精品久久久久久久不卡| 久久国产精品99精品国产|