2011-09-23 28 views
2

假設我們有一個加載託管DLL的非託管進程。在DLL中,需要阻止非託管應用程序的部分或全部線程同步對某些內存的併發訪問。 如何阻止這些非託管線程?我只有非託管應用程序的二進制文件,因此不可能重新編譯。C#阻止非託管代碼的線程

+1

你的意思是阻止是暫停非託管線程或其他東西? – Vasya

+0

是的,我想暫停非託管線程,同時做一些寫入內存的東西。之後,無人管理的線程可以繼續。 – Juergen

+6

你不能通過掛起線程來實現線程安全的代碼。這隻會造成僵局和種族混亂。使用操作系統提供的同步原語。兩個命名互斥體都可用。 –

回答

3

我叫你注意文檔的SuspendThread API函數的「備註」部分:

此功能主要是用來調試程序的使用。它不是用於線程同步的(重點煤礦)

這應該向你建議,你想的是一個壞主意。所以應該暫停線程託管API(Thread.Suspend)事實證明是如此的問題,微軟實際上從.NET Framework中刪除它。

事實是,您無法安全地掛起與您相同的進程中的線程,而無需該線程的某種合作。舉一個例子,你可能最終掛起另一個線程,而它正處於分配內存的中間,並且對堆的控制結構有一個鎖。 Bam:你的線程會死鎖,如果你試圖做任何分配內存的東西,其中包括JIT編譯你的.NET代碼。 (和堆鎖只是一個可能的死鎖了許多。而且有競爭條件。只是不要去做。)

正如漢斯在上面的評論說,你無法實現線程安全代碼通過暫停線程。您必須通過共享一個同步對象(互斥體,超薄閱讀器/寫入程序鎖定,監視器等)並讓雙方適當地向同步對象發出「我開始訪問此共享資源」的信號並與其他線程合作,並且「我受夠了」。如果一方不合作,那麼你不能有線程安全。


如果你真的必須暫停非託管的應用程序更新存儲器位置,你真的不能讓該應用程序的合作,最安全的方式很可能是關於寫作的進程中DLL忘記,和而是看看debugger APIHere's這篇文章演示瞭如何使用.NET中的一些調試器API。

讓您的應用成爲一個單獨的進程,作爲調試器附加到您的非託管應用(通過在應用啓動後附加或在調試控制下啓動應用),然後可以從外部控制它,就像您在Visual Studio調試器下運行非託管應用程序 - 暫停,修改內存,恢復。現在你處於一個單獨的進程中,你不會與你試圖掛起的應用程序共享堆鎖(或大多數其他死鎖問題) - 所以避免SuspendThread的原因很大程度上消失了。既然你寫一個調試器(各種),現在調用SuspendThread是適當的。

但你仍然必須自己處理競賽條件。仔細想想一下,如果另一個線程正在寫入你的內存塊的一半,會發生什麼。 (當你恢復該線程時,它會寫另一半,破壞你剛剛放在那裏的數據。)如果另一個線程正在讀取你的內存塊 - 如果讀取了前幾個字節,根據它們做出了一個決定,並且正在讀取接下來的幾個字節呢?如果它只是讀取這個內存,在它的寄存器中有它的一個副本,並且正在寫回它的變化? Tread carefully.

+0

感謝您長時間的回答,非常翔實。我不知道該怎麼做,因爲我沒有非託管應用程序的源代碼。所以我不能爲它創造一個互斥鎖(或者我可以)。而且由於業務限制,我無法將DLL移動到單獨的進程中。 – Juergen