malloc和free是線程安全的嗎,在多線程開發時用這兩個函數應該注意什麼?


是,不需要注意


目前看到的主流平台Win/*nix的主流編譯器都是線程安全的。

部分規模較大的嵌入式環境也是線程安全的。

少部分微型的嵌入式操作系統不是線程安全的(因為可能沒有多線程的概念)。

因為線程安全,所以malloc和free會加鎖(具體是哪種鎖就看情況了),多核的情況下會嚴重降低效率,所以不要用的太多。


是的。

如果並發量高、分配頻繁,可以考慮用tcmalloc。


線程安全,但不是可重入(reentrant),因此也不是Async-Signal-Safe,這個還是要注意的。


以下針對Windows:

線程安全

Windows上VC的malloc調用的是HeapAlloc

vista開始微軟把低碎片堆作為默認,性能不錯,產生的內存碎片也較少。

注意事項的話主要是性能方面了。我覺得優化的話分為三個粒度。

最大的粒度是從內存管理器級別,如google的tcmalloc,intel的tbb等,基本上也都是利用TLS來最大幅度的減少爭奪。好處是效果是全局的,對程序員透明的,但不好處是需要對程序侵佔式的接管malloc的調用(比如程序啟動時對malloc打補丁在原malloc處jump到tcmalloc自己的api),鉤住多個Windows API的調用,一定程度上影響穩定性,這種行為也不一定會被安全模塊所允許,據我所知GPK就不允許所保護的遊戲客戶端在運行期間修改函數的跳轉地址;另外因為很多情況線程申請了內存會在另一個線程釋放,內存管理器只能根據某種策略定期平衡這個問題,這會導致內存佔用的增加;內存管理器複雜度劇增,穩定性下降。

中間的粒度是通用內存池,也可以利用TLS來進行優化,但如上所述不一定適合,如果用不上TLS,其實我覺得除非對實時性要求很高的程序大可不必引入普通的內存池,因為一方面內存池也是把鎖從HeapAlloc內部轉移到了內存池,另一方面低碎片堆內部也存在各種分配內存優化的數據結構(如按照不同的塊大小規模分成不同的待分配鏈表等),也不是調用一次HeapAlloc就VirtualAlloc一次,做的事情跟應用層面的內存池相似。

最小的粒度是專用的內存塊/池。如果能根據業務特點,一個會話內或一個業務上下文內能串列的重複利用一塊或多塊內存的話,可以預先分配一組或若干內存塊來避免malloc。比如boost.asio里一個連接的非同步操作需要一塊固定的內存用作臨時數據的存儲,提供了一個asio_handler_allocate鉤子。而合格的asio編程實作中,一個連接同時只投遞一個讀請求或寫請求等讀到數據或寫完數據時才投遞下一次讀請求或寫請求,因此這兩個業務各自是串列的。這種情況下,只需要在連接對象創建時固定分配兩塊內存,各自用於讀請求和寫請求,就可避免掉asio內部對malloc的調用。

至於內存碎片的話,如果這真的成為問題,一個是把老古董server2003扔了換新點的windows,另一個是編譯成64位程序。至少我不覺得這是一個問題。


我就回答下調用free函數要注意的地方吧。free函數如果誤用的話會出現以下的錯誤:

這是我以前寫的一個程序,調用了free之後報的這個錯,後來查明原因是沒有從申請的內存地址的開頭開始釋放。我把這個問題記錄在我知乎的文章上:知乎專欄 希望對你有幫助


多線程高並發請使用內存池;或者(如果可以)直接一次性分配好內存,後面復用。

1.malloc/free會導致系統用戶態/核心態切換,消耗大。

2.malloc/free線程安全意味著他要加鎖,那麼你會看到任務管理器里cpu鋸齒形狀。

3.不斷的malloc/free運行久了會內存碎片。當然現在64位系統地址空間夠大,要求不是很苛刻的話問題不大


為什麼不自己驗證呢


是線程安全的,

但不同線程之間調用malloc/free, 實際會有鎖的,影響性能!


線程安全,但不可重入。

使用信號的時候需要特別注意。


線程安全,和不安全

影響效率。

看你的程序對性能的要求。


推薦閱讀:

Python有GIL為什麼還需要線程同步?
像網路爬蟲、資料庫等方面多線程程序如何設置線程數?
UDP socket能否被多線程同時調用sendto來發送數據?
Linux 開發,使用多線程還是用 IO 復用 select/epoll?
如何修改shared_ptr智能指針,讓他支持多線程?

TAG:編程 | 伺服器 | 多線程 | malloc | 線程安全 |