大伙估計在多任務程序中使用printf打印一些信息是非常歡樂的一件事,運氣不錯的話偶爾錯幾個數據、亂幾個碼也不是什么大問題,倒霉點的可能就直接掛機、卡死了,那這些到底是什么原因導致的呢? 今天的這篇文章應該可以幫助你解決一大部分問題。
01
兩個概念
1、可重入函數 多任務系統中每個進程或線程都是由多種執行流并發運行的,當執行流同時進入同一個函數(大部分是由于中斷),而不會導致函數結果不確定或者錯誤,就可認為該函數是可重入的。
上圖中的signal一般都是因為中斷等原因產生的,在我們多任務系統中中斷更是無時無刻的在隨機發生著,一旦線程或者進程被中斷轉而去執行消息處理,而恰巧的是此時線程所執行的操作與信號處理相同,比如malloc,他們都共享同一個資源(全局堆空間)。 當信號處理返回到原來的執行流中卻由于信號處理的改寫而受影響,比如重入的問題導致malloc會分配相同的內存區域,或者其他的數據篡改,鎖死等,這樣該函數就是不可重入的。 2、線程安全 多個線程并發運行且執行相同的代碼,而不會導致運行結果受影響,我們就認為是線程安全。
對于線程安全往往加個互斥鎖就可以解決戰斗,當然你如果沒有使用全局變量、靜態變量等等共享資源,那他們基本上是線程安全的。 其實從表面上看這兩個概念并沒有太大的差異,無非就是運行同一塊代碼,會不會導致不確定的結果。 3、VS 再仔細想想,其實這兩個概念所關注的層面是不同的,可重入函數要求相同執行流執行不會受影響,而線程安全僅僅只是在線程這個層面上進行的要求。 所以可重入的要求比線程安全要更加嚴苛,可重入必定線程安全,而線程安全并不一定可重入,如下圖是他們的關系:
02
不可重入死鎖
下面我們來簡單分析一下不可重入死鎖的問題 :
上圖我們為函數加了互斥lock(不考慮遞歸鎖),那么它就是線程安全的,然而剛把共享資源部分上鎖,隨機的中斷信號處理發生了,轉而執行信號處理函數,而在信號處理中也同樣執行相同的Fuction代碼,此時資源已經鎖住,必須等待前面調用Fuction函數的線程釋放,但此線程又在等待信號處理訪問,最終死鎖,涼涼! 當然你可以使用遞歸鎖來進行處理,這在正常設計中是需要避免的,當時對于一些需要調用外部庫的設計,只能選擇遞歸鎖等。
03
可重入的識別
說實在對于很多玩RTOS的伙計,直接關中斷、開中斷的臨界資源保護就基本告別了一部分不可重入問題,而這樣的操作會影響多任務的并發執行,但是如果你只是加了幾把鎖,可能在程序中不重入的問題還是要注意一下。 這里不可重入問題不完全總結了幾點:
1、標準IO函數都會使用到全局的數據結構,比如printf函數 : 由于使用了全局標準輸出stdout,所以線程不安全也就更不能重入了。
2、malloc和free : 這兩個函數都是在全局的堆空間上進行操作,如果有加鎖那就是線程安全的,但是不可重入。
3、對于全局、靜態的資源的訪問都會導致線程不安全,比如一些函數使用的是靜態緩存區等,你可以使用本地copy加臨界區來進行保護,盡量使用局部變量。
4、調用不可重入或者線程不安全的函數也會繼承對應特點。
責任編輯:xj
原文標題:同事"可重入"與"線程安全"老分不清,導致一堆bug!
文章出處:【微信公眾號:嵌入式ARM】歡迎添加關注!文章轉載請注明出處。
-
函數
+關注
關注
3文章
4306瀏覽量
62430 -
BUG
+關注
關注
0文章
155瀏覽量
15651 -
線程安全
+關注
關注
0文章
13瀏覽量
2456
原文標題:同事"可重入"與"線程安全"老分不清,導致一堆bug!
文章出處:【微信號:gh_c472c2199c88,微信公眾號:嵌入式微處理器】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論