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

            桃源谷

            心靈的旅行

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

            準(zhǔn)則6:遵守多線程編程的常識(上)

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

            UNIX上C++程序設(shè)計守則(6)Add star

            準(zhǔn)則6: 遵守多線程編程的常識


            1. 要準(zhǔn)確把握在POSIX標(biāo)準(zhǔn)的函數(shù)中,那些函數(shù)是非線程安全的,一定不要使用
            2. 要讓自己編寫的函數(shù)符合線程安全
              • 在訪問共享數(shù)據(jù)/變量之前一定要先鎖定
              • 如果使用C++的話,一定要注意函數(shù)的同步方法

            說明: (1) 要準(zhǔn)確把握那些非線程安全的函數(shù),一定不要使用


            如果在POSIX平臺上進行多線程編程時,有幾個最基本的知識,也就是所說的“常識”,希望大家一定要嚴(yán)格遵守。


            ...首先、我們要理解“線程安全”的意思。線程安全的函數(shù)就是指,“一個能被在多個線程同時調(diào)用也不會發(fā)生問題的函數(shù)”。這樣的函數(shù)通常要滿足以下幾個的特質(zhì)。

            1. 不要操作局部的靜態(tài)變量(函數(shù)內(nèi)的static變量)和全局靜態(tài)數(shù)據(jù)(全局變量,函數(shù)外的靜態(tài)變量)。而且,也不要調(diào)用其他的非線程安全的函數(shù)
            2. 如果要操作這樣的變量的話,事先必須使用互斥鎖mutex進行同步,否則一定要限制多個線程同時對它的訪問

            那么、在POSIX標(biāo)準(zhǔn)的函數(shù)里面,也有不滿足上述條件的。由于歷史遺留問題,一些函數(shù)的識別標(biāo)識(signature)的定義沒有考慮到線程安全的問題,所以不管怎么做都不能滿足上述的條件。例如,看看 localtime函數(shù)吧。它的定義的識別標(biāo)識(signature)如下:

            struct tm *localtime(const time_t *timer);

            localtime 函數(shù)是,把一個用整數(shù)形式表示的時刻(從1970/1/1到現(xiàn)在為止的秒數(shù))、轉(zhuǎn)換成一個能讓人容易明白的年月日形式表示出來的tm結(jié)構(gòu)體并返回給調(diào)用者的函數(shù)。根據(jù)規(guī)格說明、返回出來的tm結(jié)構(gòu)體是不需要free()掉,也不能釋放的。這個函數(shù)典型的實現(xiàn)就像下面的代碼那樣:

            struct tm *localtime(const time_t *timer) {
            static struct tm t;

            /* ... 從timer參數(shù)里算出年月日等數(shù)值 ... */

            t.tm_year = XXX;
            /* ...把它們填入到結(jié)構(gòu)體內(nèi)... */
            t.tm_hour = XXX;
            t.tm_min = XXX;
            t.tm_sec = XXX;

            return &t;
            }

            這個函數(shù)如果被像下面那樣使用的話,就會有漏洞:

            1. 在線程A里執(zhí)行 ta = localtime(x);
            2. 在線程B里執(zhí)行 tb = localtime(y);
            3. 線程A參照ta結(jié)構(gòu)體里的數(shù)據(jù) → 就發(fā)現(xiàn)這些數(shù)據(jù)是一些奇怪的值!

            ...在函數(shù)的說明手冊里對這個問題也沒有做過詳細(xì)的說明。關(guān)于這個漏洞,在localtime函數(shù)即使使用了mutex鎖也不能被回避掉。所以,這個函數(shù)定義的識別標(biāo)識是不行滴。
            [譯者lymons注:在多個線程里調(diào)用localtime函數(shù)之所以有問題的原因是,localtime函數(shù)里返回的tm構(gòu)造體是一個靜態(tài)的結(jié)構(gòu)體,所以在線程A里調(diào)用localtime函數(shù)時,該結(jié)構(gòu)體被賦予正確的值;而在線程A參照這個結(jié)構(gòu)體之前,線程B又調(diào)用localtime的話,這個靜態(tài)的結(jié)構(gòu)體又被賦予新的一個值。因此在線程A對這個結(jié)構(gòu)體的訪問都是基于一個錯誤的值進行的]


            正因為如此,就像上面說過的POSIX規(guī)格(SUSv3)里整齊的

            asctime, basename, catgets, crypt, ctime, dbm_clearerr, dbm_close, dbm_delete, dbm_error, dbm_fetch, dbm_firstkey, dbm_nextkey, dbm_open, dbm_store, dirname, dlerror, drand48, ecvt, encrypt, endgrent, endpwent, endutxent, fcvt, ftw, gcvt, getc_unlocked, getchar_unlocked, getdate, getenv, getgrent, getgrgid, getgrnam,

            (省略)

            對于在規(guī)格中被定義為非線程安全的函數(shù),應(yīng)該制定一個避免使用它們的規(guī)則出來,并且制作一個能夠自動檢查出是否使用了這些函數(shù)的開發(fā)環(huán)境,應(yīng)該是比較好的。


            反之,在這里沒有被登載的POSIX標(biāo)準(zhǔn)函數(shù)都被假定為 "shall be thread-safe" 的、所以在實際的使用中可以認(rèn)為在多線程環(huán)境里是沒有問題的(而且在使用的平臺上沒有特別地說明它是非線程安全的話)。


            另外,有幾個非線程安全的函數(shù),都準(zhǔn)備了一個備用的線程安全版本的函數(shù)(僅僅是變更了函數(shù)的識別標(biāo)識)。像這些函數(shù)為了與原版進行區(qū)別都在其函數(shù)名后面添加了 _r 這個后綴*1。例如,asctime函數(shù)就有線程安全版本的函數(shù)asctime_r。在規(guī)格說明中是否定義了備用函數(shù),可以試著點擊剛才的那個網(wǎng)頁里面的函數(shù)名就可以看到。點擊 rand函數(shù)就可以看到,

            [TSF] int rand_r(unsigned *seed);

            用[TSF]這樣的文字標(biāo)記出來的函數(shù)吧。這就是備用函數(shù)。在一覽中沒有記載出來的函數(shù)(備注: 稍微有點兒出入。請參照這里)、據(jù)我所知還有下面的備用函數(shù)。

            asctime_r, ctime_r, getgrgid_r, getgrnam_r, getpwnam_r, getpwuid_r, gmtime_r, localtime_r, rand_r, readdir_r, strerror_r, strtok_r

            還有,在規(guī)格以外,還準(zhǔn)備了很多的下面那樣的函數(shù)。

            gethostbyname_r, gethostbyname2_r

            在最近的操作系統(tǒng)中、也使用 getaddrinfo API函數(shù)來解決IPv6名字對應(yīng)的問題。gethostbyname系列的API都是比較陳舊的函數(shù)了、所以使用前面的函數(shù)還是比較好吧*2。根據(jù)規(guī)格SUSv3,getaddrinfo也是線程安全的:

            The freeaddrinfo() and getaddrinfo() functions shall be thread-safe.

            在多線程編程中,不要使用非線程安全的函數(shù),而他們的備用函數(shù)可以放心地積極的去使用。


            后續(xù)

            *1:在C言語里函數(shù)不能重載,所以只能添加一個新的函數(shù)

            *2:跟網(wǎng)絡(luò)有關(guān)的API哪些是新的哪些是舊的,可以參考 IPv6網(wǎng)絡(luò)編程 (network technology series) 這本好書

            我的個人簡歷第一頁 我的個人簡歷第二頁
            亚洲精品乱码久久久久久自慰| 精品久久久久久| 久久噜噜电影你懂的| 久久久久久久综合日本| 无码人妻久久一区二区三区免费丨 | 色99久久久久高潮综合影院 | 久久婷婷色综合一区二区| 久久青青草原精品影院| 亚洲国产成人久久一区久久| 亚洲国产一成人久久精品| 久久人人妻人人爽人人爽| 99久久精品费精品国产| 99麻豆久久久国产精品免费| 亚洲狠狠婷婷综合久久久久| 国产叼嘿久久精品久久| 久久精品国产亚洲AV大全| 亚洲人成电影网站久久| 激情久久久久久久久久| 99久久免费国产精品热| 久久人人爽人人爽人人片AV高清| 色欲综合久久中文字幕网| 久久久91人妻无码精品蜜桃HD | 久久午夜伦鲁片免费无码| 欧美激情精品久久久久久久九九九| 99精品久久久久久久婷婷| 合区精品久久久中文字幕一区| 久久国产高清一区二区三区| 久久久精品国产亚洲成人满18免费网站 | 久久久久亚洲av毛片大| 精品久久久久久久久免费影院| 国产精品国色综合久久| 久久久久久久精品成人热色戒| 国产精品九九久久免费视频| 久久久久亚洲av无码专区导航 | 久久久久亚洲AV无码观看| 久久久久久a亚洲欧洲aⅴ| 亚洲精品无码久久一线| 久久久久久久精品成人热色戒| 午夜精品久久久久| 超级碰碰碰碰97久久久久| 亚洲国产成人久久一区WWW|