2011-03-14 59 views

回答

1

Brian Lambert寫了一篇博文,標題爲A simple and totally thread-safe implementation of IDisposable
它包含以下實現:

using System; 
using System.Threading; 

/// <summary> 
/// DisposableBase class. Represents an implementation of the IDisposable interface. 
/// </summary> 
public abstract class DisposableBase : IDisposable 
{ 
    /// <summary> 
    /// A value which indicates the disposable state. 0 indicates undisposed, 1 indicates disposing 
    /// or disposed. 
    /// </summary> 
    private int disposableState; 

    /// <summary> 
    /// Finalizes an instance of the DisposableBase class. 
    /// </summary> 
    ~DisposableBase() 
    { 
     // The destructor has been called as a result of finalization, indicating that the object 
     // was not disposed of using the Dispose() method. In this case, call the DisposeResources 
     // method with the disposeManagedResources flag set to false, indicating that derived classes 
     // may only release unmanaged resources. 
     this.DisposeResources(false); 
    } 

    /// <summary> 
    /// Gets a value indicating whether the object is undisposed. 
    /// </summary> 
    public bool IsUndisposed 
    { 
     get 
     { 
      return Thread.VolatileRead(ref this.disposableState) == 0; 
     } 
    } 

    #region IDisposable Members 

    /// <summary> 
    /// Performs application-defined tasks associated with disposing of resources. 
    /// </summary> 
    public void Dispose() 
    { 
     // Attempt to move the disposable state from 0 to 1. If successful, we can be assured that 
     // this thread is the first thread to do so, and can safely dispose of the object. 
     if (Interlocked.CompareExchange(ref this.disposableState, 1, 0) == 0) 
     { 
      // Call the DisposeResources method with the disposeManagedResources flag set to true, indicating 
      // that derived classes may release unmanaged resources and dispose of managed resources. 
      this.DisposeResources(true); 

      // Suppress finalization of this object (remove it from the finalization queue and 
      // prevent the destructor from being called). 
      GC.SuppressFinalize(this); 
     } 
    } 

    #endregion IDisposable Members 

    /// <summary> 
    /// Dispose resources. Override this method in derived classes. Unmanaged resources should always be released 
    /// when this method is called. Managed resources may only be disposed of if disposeManagedResources is true. 
    /// </summary> 
    /// <param name="disposeManagedResources">A value which indicates whether managed resources may be disposed of.</param> 
    protected abstract void DisposeResources(bool disposeManagedResources); 
} 

但是絕對和完整的整體是有爭議的評論了一下,無論在博客和這裏。

+3

@Hans:這不是根本錯誤;這是完全可能的,多線程希望在它們之間進行談判誰得到處置對象時,他們都與它在同一時間全部完成。 **談判協議必須由線程安全的部分組成。** Brian的博客不是「動盪」。 – 2011-03-14 14:55:11

+1

這不是真正的線程安全的,因爲它沒有正確地解決其他方法調用該類的行爲必須如何。如果你真的想在Dispose上擁有線程安全性,最簡單的方法是將整個Dispose方法封裝在一個鎖中,並在每個公共方法周圍使用相同的鎖,並拋出ObjectDisposedException(如果已經處理)。如果您需要允許方法與Dispose方法並行執行,則可以使用MultiRead/SingleWrite模式,其中Dispose是「寫入」操作。 – 2011-03-14 14:56:21

+0

@Eric:如果此協商正確實施,那麼它不需要IDisposable實現是線程安全的。談判需要。我沒有斷言Brian的博客是順便說一句。對不起,冒犯了你。 – 2011-03-14 14:59:35

29

在完成之前,您不應該處置對象。如果有其他線程引用該對象,並且有可能需要調用它的方法,則不應該對其進行處理。

因此,Dispose不是必須的線程安全。

+2

在一些情況下,優選的方式(如果不是唯一的方式)來強制一個阻塞I/O操作的放棄是'Dispose'從它的下面的連接的;例如可處置* *只從外螺紋發生(在阻塞的線程不能做任何事情,而它的阻止)。這是可能的外螺紋可能決定'Dispose'就像連接完成了其工作,並已被阻塞線程的連接決定'Dispose'它本身。 – supercat 2012-06-15 17:32:04

+0

如果有一個用例另一個線程迫使阻塞操作的放棄,這需要有人來編寫代碼,使其工作。當然實施者將是明智的實現'ForceAbandon'方法拉澤而不是濫用'Dispose'的語義。如果實現者沒有意識到這是一個必需的用例,那麼你不能指望'Dispose'也能正確運行。它可能,但那只是運氣。 – Ben 2012-06-15 18:53:31

+1

我見過很多I/O庫,其中唯一支持的跨線程操作正在終止連接。 「處置」似乎是最合適的。此外,在某些情況下,要保證局部構造的對象的清理最合理的方式可能涉及相互處置對方的對象,所以'Object1.Dispose'可以稱之爲'Object2.Dispose',這可能依次調用'Object1.Dispose '。防止這種情況不需要「互鎖」,但它仍然需要比微軟更好的模式。 – supercat 2012-06-15 19:42:33

4

線程安全的Dispose模式的唯一真正好處是您可以保證在發生跨線程誤操作時獲得ObjectDisposedException而不是潛在的不可預知的行爲。請注意,這意味着該模式需要超過線程安全的Dispose;它要求所有依賴不被處置的類的方法與處理機制正確地互鎖。

可以做到這一點,但是要覆蓋只有在存在使用錯誤(即錯誤)時纔會發生的邊界情況是很費事的。

+0

關於所涉及的努力:我們可以使用PostSharp將驗證邏輯注入每個公共方法嗎?我在看PostSharp的方向的唯一原因是因爲我看到其他語言一直在做這類事情。 – GregC 2011-03-25 17:08:57

+0

儘管沒有很多情況下多個線程會同時發現一個對象是無用的,但是在很多情況下,對象的最後一次使用可能會異步發生,或者一個對象有資格進行垃圾回收將意味着仍然存在強烈參照的另一個對象已經變得毫無用處。保證線程安全的「IDisposable」在這種情況下會很有用。 – supercat 2012-06-15 17:39:44

0

我不確定Microsoft爲什麼不在非虛擬處置方法中使用互鎖Disposing標誌(打算終結器 - 如果有的話 - 應該使用相同的標誌)。多線程可能嘗試處理對象的情況很少,但並不禁止。例如,可能會發生這樣的對象,它們應該執行一些異步任務並自行清理,但如果需要,可以儘早將其清除。對象處理不應該經常發生,因爲Interlocked.Exchange有任何有意義的性能成本。

。另一方面,需要注意的是,同時保護對處置多次調用是一個恕我直言明智的政策是非常重要的,它是不足以使處置真的是線程安全的。還有必要確保在正在使用的對象上調用Dispose會使事情處於良好狀態。有時最好的模式是設置一個「KillMeNow」標誌,然後在由Monitor.TryEnter守護的塊中,處理該對象。它使用該對象必須既獲取之前和解除鎖定,以查看是否KillMeNow被設定後,即可獲得操作期間的鎖,但是測試每例程(比其它處置);如果是這樣,請執行Monitor.TryEnter並執行處理邏輯。

與進行線程安全的IDisposable更大的問題是,微軟並沒有指定一個事件的RemoveHandler方法必須是線程安全的了無死鎖的風險的事實。 IDisposable.Dispose經常需要刪除事件處理程序;如果沒有這種保證線程安全的方式,寫一個線程安全的Dispose幾乎是不可能的。

相關問題