歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux技術

可重入函數和線程安全

可重入函數
當捕捉到信號時,不論進程的主控制流程當前執行到哪兒,都會先跳到信號處理函數中執行,從信號處理函數返回後再繼續執行主控制流程。信號處理函數是一個單獨的控制流程,因為它和主控制流程是異步的,二者不存在調用和被調用的關系,並且使用不同的堆棧空間。引入了信號處理函數使得一個進程具有多個控制流程,如果這些控制流程訪問相同的全局資源(全局變量、硬件資源等),就有可能出現沖突,如下面的例子所示。 如下是不可重⼊入的函數:

main函數調用insert函數向一個鏈表head中插入節點node1,插入操作分為兩步,剛做完第一步的時候,因為硬件中斷使進程切換到內核,再次回用戶態之前檢查到有信號待處理,於是切換到sighandler函數,sighandler也調用insert函數向同一個鏈表head中插入節 點node2,插入操作的兩步都做完之後從sighandler返回內核態,再次回到用戶態就從 main函數調用的insert函數中繼續往下執行,先前做第一步之後被打斷,現在繼續做完第二
步。結果是main函數和sighandler先後 向鏈表中插入兩個節點,而最後只有一個節點真正插入鏈表中了。
函數被不同的控制流程調用,有可能在第一次調用還沒返回時就再次進入該函數,這稱為重入,insert函數訪問一個全局鏈表,有可能因為重入而造成錯亂,像這樣的函數稱為不可重入函數,反之,如果一個函數只訪問自己的局部變量或參數,則稱為可重入 (Reentrant) 函數。
在大部分情況下,不可重入的函數修改成可重入的函數時,需要修改函數的對外接口。不可重入的函數不能被多線程的程序調用。一個不可重入的函數,基本上不可能是線程安全的。
線程安全
一個線程安全的函數通過“鎖”來保護共享的資源不被並發地訪問。“線程安全”僅關心函數的實現,而不影響它的外部接口。
線程安全函數要解決的問題是,多個線程調用函數時訪問資源沖突。
使用靜態數據或者其它任何共享資源(如文件、終端等)的函數,必須對這些資源加“鎖”以實現對它們的串行訪問,這樣才能成為線程安全的函數。
可重入和線程安全(Thread-Safe)是兩個不同的概念:可重入函數一定是線程安全的;線程安全的函數可能是重入的,也可能是不重入的;線程不安全的函數一定是不可重入的。
可重入與線程安全兩個概念都關系到函數處理資源的方式。但是,他們有一定的區別。可重入概念會影響函數的外部接口,而線程安全只關心函數的實現。
大多數情況下,要將不可重入函數改為可重入的,需要修改函數接口,使得所有的數據都通過函數的調用者提供。
要將非線程安全的函數改為線程安全的,則只需要修改函數的實現部分。一般通過加入同步機制以保護共享的資源,使之不會被幾個線程同時訪問
在一個多線程的程序中,所有的被多線程調用的函數多必須是線程安全的(或可重入的)。注意,不可重入的函數一般都是線程“不安全”的,然而,將它們改寫成可重入的同時,一般就會將它們變成線程安全的。
結論:
可重入函數要解決的問題是,不在函數內部使用靜態或全局數據,不返回靜態或全局數據,也不調用不可重入函數。線程安全函數要解決的問題是,多個線程調用函數時訪問資源沖突。
Copyright © Linux教程網 All Rights Reserved