我一直沒試過在多緒環境下使用 rand() 或 strtok() 這類函數,因為古有明訓,早在我對 thread 都還沒有什麼概念之前,就已經知道 rand() 或 strtok() 最初設計時對多緒環境完全盲目,所以這種用法對我來說是根本都不用考慮的愚蠢行為。另一方面是因為手邊有比 rand() 和 strtok() 更強而有力的工具,所以甚至是普通的單緒程式我都不太喜歡用 rand() 和 strtok()。
這些函數在多緒環境的問題很容易理解,例如 rand() 通常用線性同餘法實作,內部要維護一個長期存在的種子以產生下一個數。麻煩的地方在於,在多緒環境下呼叫這些函數,可能導致於同時競爭讀寫這些記憶體。同樣的問題也發生在 strtok 上。
不過最近試了一下,有點意外的發現只要連結到多緒版的 VC runtime (這是visual studio預設值),其實並不會有問題。看起來似乎是每一個 thread 都有獨立的記憶體給 rand() 和 strtok() 使用,不同執行緒彼此之間完全不會互相干擾,所以沒有執行緒競爭讀寫的問題。
(這好像已經是常識了,真汗顏啊。因此,又多了一項我對人嗆聲時,會被人回嗆「幹!你白癡啊,老子用這麼久都沒怎樣」的東西了。)
這也表示在原 thread 裡面使用 srand() 對後來分出的 thread 不會有任何作用,新 thread 裡的 rand() 擁有一組完全獨立的資料,必須要重新取種子,否則就只會跑出固定的序列。同樣的,新 thread 裡面的 strtok() 對於分家之前餵給 strtok() 的字串一無所知。
這個事實剛好讓我想通另一項長期以來只知其然,卻不確定原因的常識:用 CreateThread() API + 多緒的 VC runtime 會造成少許的 memory leak (小小的 memory leak 當然是不會怎麼樣,君不見網路上好幾貨櫃的教學都還是拿 VC Runtime 和 CreateThread() 混著用,我常看到臉上三條線.....)。
像 rand、strtok 這類的 VC runtime 函數都會配置 thread 專屬的記憶體空間,然而 CreateThread() 這類的系統級 API 才不管這種語言環境專屬的鳥事。雖然微軟保證 VC runtime 可以和 CreateThread()合作愉快,但是這些系統 API 畢竟對於額外配置的記憶體一無所知,所以 thread 結束後不會去收拾殘局,而造成 memory leak。
所以程式中只要使用了 VC runtime,最好還是用 VC runtime 自家的 _beginthreadex() 來發起執行緒,不要用 CreateThread(),_beginthreadex() 懂得如何處理這些額外配置的記憶體。而且別以為用 VC 以外的編譯器就沒事,其他C/C++編譯器或其他語言很可能也還是會在底層連結VC runtime。
最後我還是覺得在多緒下使用 rand() 和 strtok() 絕不是好主意,並不是每家的編譯器都妥善處理這個問題。以下是常見可能會有問題的函數
rand
strtok
asctime
ctime
gmtime
localtime
留言列表