http://aftcast.pixnet.net/blog/post/23786004
何謂thread-safe? 這個問題我看過許多論壇都有討論過,都總讓人覺得不很滿意。在此,筆者想要用更logical的方式來把議題說清楚。首先,我要了解它的定義! 定義若都不明白就難以判別安不安全了!
thread是什麼呢? 可能也有人不太了解。就從這裡開始… 當cpu處理一段(區塊)的程式碼時,從開始的第一行程式碼來跑就算是thead的開始,直到區塊的最後一行程式結束,就算是thread的結束。所謂的「主thread」,在dos (或dos like)程式裡,就是指main( )這個函式的開始到結束的一個thread。而主thread以外的一個子thread則是指程式人員自行在主thread裡再定義一個「程式區塊」,並請cpu同步的去執行那個區塊。
由上面的thread的定義來看,我們可以進而推論所謂的multithread這個字其實是有三種情形的,哪三種呢?
1/ 相同的一個程式區塊,請cpu"分出多身"來執行它!
2/ 不同的程式區塊,請cpu各別分時的去執行每個區塊
3/ 混合以上二種情形
所以,當我們在討論多緒/多線程(multithread)的問題時,我們要先去了解問題是上面三類的哪一類。許多的情形下,都是指第一類!
我們再把「程式區塊」給解析一下。它可以是一個function,或是一個class,或是上述二者的混合體。因為function或是class都會使用到變數,於是多緒時變數的共享就成了一個很大的問題。這就是為什麼會有thread-safe的這個議題了!
到此為止,我們可以想像若一個程序(process / 主thread)裡,有大於等於一個子thread在跑,就會產生thread-safe的問題。以最簡單的情形來說,mainthread+user自定的一個thread。假設Button元件按下後會寫入一個item至memo元件裡,而同時間的一個thread則會取用memo裡的item。假想當thread在取用item做分析的時候,user不知情的按下button,那麼thread是否有可能會取到不正確的item? 若不經過特別的安全設定,肯定「取用」時時而會出問題!
因此,到底安全或不安全的根源就在那執行的區塊程式碼上了(以上例就是取用的過程)。我要補充一個重要的事,其實每一個thread都有自己的一個專屬stack,所以auto級(local)的區域變數是不會有共享的問題,因為它們都是存放在thread各自的記憶體空間內,彼此不甘擾。也就是說,若你的程式區塊中,所有使用到的變數都是local(auto)變數,那你這個區塊,或說這個thread,就算是thread-safe! 相反的,若你的區塊中有用到全域的變數,那麼你的這個thread就有可能是不安全。請注意看,是有可能,並非一定是。
如何把含有全域變數的thread分析出安全或不安全?其實這是很直覺的。比如說某全域變數int g被thread使用到,但整個thread裡只參考到一次,那算安全嗎? 答案是…安全。因為單一基本變數的一次存取cpu處理極快,不太會有衝突。又,若是某全域變數 struct tag 被參考到一次,那算安全嗎? 答案是…不太安全,因為一個結構體的存取可能涉及到多個微指令。進一步的用上面的例子來說,若int g被參考到2次(含)以上,那就肯定不安全了。比如說第一次是把g設成k,第二次把g再設給某變數i,若thread1正在處理第二次參考,而thread2在處理第一次參考。那thread1可能會得到意外的答案!
如何讓不安全的thread變成安全的?? 其實主要的方法有二類。
1/ 存取共用全域變數時加上lock的機制,而這lock的機制在windows上可以使用mutex、criticalsection、event、semaphore等物件配合WaitForSingleObject或WaitForMultipleObject二個api來控制。其原理就是當某thread在取用資料時,其他的通通都停止並等待,直到該thread取用完。
後註: class 其實也像是一種function,所以class本身也是存在著安全與否的問題。倘若該class裡的menthod有用到全域的變數時,該class也算是不安全的! 而許多的VCL就是這類的class!
註2: 若什麼是auto變數、stack都還不清楚,請先自行google或查程式語言的書,這是很基本且重要但易被忽略的章節。
沒有留言:
張貼留言