準則5: 盡可能避免執行緒中做延遲撤銷的處理
說明:在前面我們已經講過,執行緒的撤消分為「非同步」「延遲」這兩種型別、並且「非同步撤消」也是非常容易引起各種複雜問題的元凶。
那麼,現在要在程式中除掉「延遲撤消」。延遲撤消雖然不會像非同步撤消那樣會引起各種各樣的問題、但是、注意事項還是有很多的。只有把下面的這些注意事項全部都把握之後才能放心使用。
注意事項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*) ; nanosleep(&ts, 0); // 經常是撤消點在上面的樣例**中、nanosleep執行的過程中經常會觸發延遲撤銷的最終動作,但是這個時候的mutex鎖還處於被鎖定的狀態。而且、執行緒一被延遲撤消的話就意味著沒有人去釋放掉這個互斥鎖了*2pthread_mutex_unlock(&mutex); // 此處不是撤消點
return 0;
}int main(void)
。因此、在下面的main函式中呼叫同樣的cancel_unsafe函式時就會引起死鎖了。
為了迴避這個問題、利用pthread_cleanup_push函式在撤消時釋放掉互斥鎖的話就ok了,也就不會死鎖了。
// 新增清除函式void cleanup(void* mutex)
// 粗體字部分是新增的語句
void* cancel_unsafe(void*) ; nanosleep(&ts, 0);
pthread_mutex_unlock(&mutex);pthread_cleanup_pop(0);return 0;
}
注意事項3: 實現要清楚延遲撤消和c++之間的相容度
使用c語言的場合,利用上面的pthread_cleanup_push/pop函式就能安全地執行延遲撤消的動作,但是在c++語言的場合就會出現其他的問題。c++與延遲撤消之間的相容度是非常差的。具體的表現有以下兩個問題:
執行延遲撤消的時候,記憶體棧上的物件的析構函式會不會被呼叫跟具體的開發環境有關係
pthread_cleanup_push/pop函式和c++的異常處理機制之間有著怎樣的相互影響也能具體環境有關
不呼叫析構函式,或者在丟擲異常的時候不能做cleanup處理,經常是發生記憶體洩漏,資源丟失,程式崩潰,死鎖等現象的原因。令人意外的是對於這個深層次的問題,就連boost c++庫都束手無策。
[q] why isn't thread cancellation or termination provided?先必須確保物件的自由儲存,而後全都讓cleanup函式去釋放物件的方法也有,但是這次是犧牲了異常安全性。[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.
(原文沒有看明白:オブジェクトを必ずフリーストア上に確保し、解體を全て、クリーンナップハンドラに行わせる手もありますが、今度は例外安全性が犠牲になるでしょう。)
應該說的是,在使用c++的工程裡不對執行緒進行延遲撤消處理還是比較實際的。
*1:好的問題是 gethostbyname()函式
準則5 盡可能避免執行緒的延遲撤銷處理
準則5 盡可能避免執行緒中做延遲撤銷的處理 說明 在前面我們已經講過,執行緒的撤消分為 非同步 延遲 這兩種型別 並且 非同步撤消 也是非常容易引起各種複雜問題的元凶。那麼,現在要在程式中除掉 延遲撤消 延遲撤消雖然不會像非同步撤消那樣會引起各種各樣的問題 但是 注意事項還是有很多的。只有把下面的這...
如何盡可能的避免漏測
在總結線上問題的時候,我們發現大部分的線上問題是由於功能漏測所導致的。原本應該測試的流程沒有測到,或者是根本沒有考慮到一些情況,這些都會產生漏測。在大部分的產品中,漏測是難以避免的,只要不出大問題,漏測的危害會被人為的粉飾和縮小,但是在某些跟貨幣或貨幣等價物打交道的行業,漏測往往意味著經濟損失,一次...
測試人員如何盡可能避免漏測
漏測是測試行業老生常談的話題,測試人員如果因為漏測出現很嚴重的生產事故,是要負全責的。一般公司都會根據生產事故的嚴重性採取一定的處罰措施。那麼問題來了實際生活中如何避免漏測?首先測試時間足夠的情況下,可以先根據需求拆分測試點,然後再根據測試點採用邊界值 等價類劃分 錯誤推測 經驗來設計測試用例。然而...