2009-11-07 71 views
2

有沒有方法可以確定free()如果曾經調用某個內存塊指針會失敗?有沒有辦法確定free()會失敗?

我有以下情況:對共享資源具有線程訪問失敗同時可能一直在釋放上述資源的狀態。現在我需要設計一個安全的方式來清理這個共享資源。

當然,我已經爲正常情況分配了資源的所有權,但上述限制情況如何呢?

更新:如果我使用額外的同步機制,它纔有更多的清理做,威力涉及額外的限制條件。如果可能,我想限制/避免這些。

分辨率:我最終決定重新執行重構。感謝所有貢獻者。你們好棒!

+0

你的意思是「一個線程失敗」是什麼意思? – Thomas 2009-11-07 15:03:39

+0

@thomas:一個線程終止,沒有時間將它的狀態傳達給負責處理的消費者。 – jldupont 2009-11-07 15:04:41

+1

這是一個壞主意。獲得線程語義/生命週期所有權,並且不要亂用標準庫中的黑客行爲。 您的問題標題和正文令人困惑 - 它們暗示您的線程在致電free()期間終止。我認爲你所說的並非如此。 爲什麼垂死的線程不能通知另一個線程? – 2009-11-07 16:49:58

回答

6

我見過各種嘗試,包括這一個:

void m_free(void **p) 
{ 
     if (*p != NULL) { 
       free(*p); 
       *p = NULL; 
     } 
} 

不僅提領一類punned指針突破各種平臺,「堵這個例子中」如果你初始化才能發揮作用,免費並重新初始化每個現有函數(包括編譯的庫)中的每個指針,並使用相同的C庫進行工作。

然後,處理優化和鎖定免費的線程安全問題。呸!

簡而言之,如果您無法跟蹤您在單個函數中分配的內容,則需要重新分配該函數的時間。這樣做夠了,你會發現對更安全的free()的需求很快消失。如果在它支持的平臺上工作,Valgrind是你的朋友。根據你的標籤,它確實是你的朋友:)

或者,使用malloc() that sports garbage collection自費,取決於你如何分配的東西和完全擺脫free()。之後,調試幾乎變得非常有趣。

希望你有重新考慮?雖然您看起來也存在相互排斥的問題,但這隻會導致重新考慮因素。也就是說,讓free()阻塞之前的行,或者嘗試獲取鎖時失敗,並且在有鎖的線程中將已釋放的指針設置爲NULL,至少在您實現的結構中。

+0

@tinkertim:有趣的推理線......讓我反思一下。 – jldupont 2009-11-07 15:35:22

+0

@judpoint:我已經編輯了我的答案以供澄清。 – 2009-11-07 15:38:09

2

我不相信有一個符合要求的界面可以做你想做的。

但是我能想到一些技巧。你可以讓容易出錯的線程直接調用free()而不是free()的包裝;包裝器可以保存地址或最後幾個地址,以便確定塊是否被釋放。你也可以阻止信號,建立一個關鍵部分,或者處理任何其他可能中斷*線程的東西。

更新:死亡的線程在退出/清理之前是否釋放了該內存?我經常爲不需要在穩定狀態下釋放的內存寫malloc前端(作爲速度優化)。如果線程剛剛設置好自己,可以在線程啓動之前將內存分配給malloc,並讓線程調用一個前端,它只是發出未鏈接的,不可移動的動態塊塊。它會跑得更快,然後當線程死亡時,您可以一次釋放整個塊。這裏的一般想法是讓容易出錯的線程通過調用可以在事後清理的服務來獲得其內存。

又一個想法:每個線程堆呢?如果線程可以被說服從他們自己的堆中分配只在他們的生命週期中需要的內存,那麼很好地將清理任務組織爲在線程重新加入父項時釋放整個線程堆。

+0

@DigitalRoss:謝謝你的建議。我相信我的問題可能不夠清楚。我會更新。 – jldupont 2009-11-07 15:07:49

+0

在free()中寫一個包裝幾乎總是會導致災難,這取決於* libc在失敗時作爲指針返回的內容,或者當它在一個操作時返回。 – 2009-11-07 15:51:31

+2

關於每個線程堆,如果某個結構體中的某些內容作爲線程參數傳遞給線程,那麼會發生什麼情況?正如Rusty所說的那樣,你可能會喜歡看「ANTI線程」。「線程吸入,antithreads儘量不要。」 http://ccan.ozlabs.org/info/antithread.html – 2009-11-08 03:02:29

1

我認爲沒有辦法完全按照你的要求去做。

問題是,沒有辦法確定死亡線程死亡時的狀態。它是否只是打電話給免費()而不再繼續? free()是否將該塊添加回空閒列表中?你不知道。


如果這是一個非常罕見的疾病的線程以這種方式死去(所以它的好身邊離開unfreed內存 - 你只是想知道不使用它),那麼以下(使用Windows電話)釋放內存和「標記」作爲自由到其他線程:

void* ptr; 
... 
void* desiredPtr = ptr; 
if(InterlockedCompareExchangePointer(&ptr, NULL, desiredPtr) == desiredPtr) 
    free(desiredPtr); 

這裏做的事情是確保只有一個線程試圖釋放內存和它之前,它設置地址爲NULL所以沒有其他線程會嘗試釋放()它。


如果這是不可接受的存儲器,可偶爾保持周圍那最好的辦法可能是有一個單獨的線程,其唯一的工作就是以釋放內存。其他線程可以爲free-memory-thread排隊免費請求。由於free-memory-thread非常簡單,它永遠不會死亡,並且可以正確完成自由操作。

0

如果您使用有效的指針調用free,我不會看到它會失敗。如果失敗,則必須由於指針無效。

除了同步對共享內存的訪問(例如,使用mutex)之外,還必須明確所有權,以避免發生雙重釋放等情況。雙重釋放是指兩個或更多線程有一個有效指針,但是隨後有多個線程嘗試釋放內存。儘管第二個線程有一個非空指針,但它不再有效。

如果您在C/C++中遇到內存問題,您可以嘗試使用像HeapAgent這樣的內存庫。像這樣的內存庫將檢測和初始化每個內存分配。在釋放內存之前,它會先檢查內存指針是否有效並且沒有緩衝區溢出錯誤。不應該更改代碼,因爲它可以簡單地替換內置的malloc/free。另外,庫可以幫助查找內存泄漏,覆蓋和無效的引用。

解決您的問題的另一種策略可能是將資源清理集中到一個線程。當一個線程與資源完成時,它只是標記它可以被垃圾收集器線程清理。

當然,然後是計劃C - 使用Java ...只是開玩笑。

+0

......這個問題的重點集中在:「我不確定指針的狀態」。如果只是由我決定,那麼我現在會在Erlang上,但在這種情況下,我別無選擇,只有C。 – jldupont 2009-11-07 19:49:12

+0

像HeapAgent所使用的策略是我能想到的唯一方法:當你malloc (x),你實際上是malloc(元數據+ x + endtag)並且用元數據(分配有多大)填充開始,並且用諸如0xDEAD之類的獨特序列來標記分配的結尾。然後你返回指針+元數據來發回x。現在,每個malloc響應實際上都指向一個真正的malloc調用的子序列。這在釋放時很有用,因爲免費通話可以執行指針數學運算來查找元數據並確認結束標記。如果一切順利,則分配將被釋放。 – AWhitford 2009-11-08 19:01:46

+0

C中的垃圾收集是危險的。它導致非常懶惰和低效的水果循環,「正常工作」。即for(i = 1; i <10; i ++){foo = strdup(「bar」); }這應該告訴你,你需要花費10分鐘的時間通過realloc()來實現對現有分配指針的操作方式,或者重新考慮你的方法:) – 2009-11-11 15:14:03

相關問題