簡體   English   中英

C#將代碼塊與引用IDisposable對象的匿名方法一起使用

[英]C# Using block with an anonymous method referencing the IDisposable object

考慮以下代碼:

using (var mre = new ManualResetEvent(false))
{
     var bgWkr = new BackgroundWorker();
     bgWkr.DoWork += delegate(object sender, DoWorkEventArgs e)
     {
         var mrEvent = e.Argument as ManualResetEvent;
         // some processing...

         mrEvent.WaitOne();
         // broadcast an event
     };
     bgWkr.RunWorkerAsync(mre);

     // some other processing...

     // hook into the same event
     mre.Set();
}

假設產生的工作人員需要一些時間才能完成。 當工作線程完成並等待ManualResetEvent時,我們將在不久前離開using塊。 我假設mre在離開using塊時會被關閉(假設它會被處置),這至少會引發異常。 這是一個安全的假設嗎?

此示例可能不是使用ManualResetEvent的最佳示例,但它說明了以下情況:我們在using塊內的匿名方法內訪問IDisposable對象,並在退出using塊后調用匿名方法。 是否有某種機制可以固定一次性物品? 我不這么認為,但是想知道為什么(如果有伏都教在工作)或為什么不這樣。

干杯,

是的,此代碼是錯誤的-結果尚未真正定義,但是將結果拋出mrEvent.WaitOne()是很合理的,因為mrEvent是幾乎肯定已mrEventManualResetEvent 從技術上講,工作線程可能已經准備就緒,並且工作線程執行“某些處理...”的速度比主線程執行“其他處理...”的速度快,但是:我不會依賴在上面。 因此,在大多數情況下: mrEvent已經死了。

至於如何避免這個問題:或許這根本就不是一個場景using 但是會發生這樣的情況,因為工作線程執行了WaitOne ,所以工作線程的WaitOne 無法在主線程執行mre.Set()調用之前完成-因此您可以利用它並將using移至工作線程:

 var mre = new ManualResetEvent(false);
 var bgWkr = new BackgroundWorker();
 bgWkr.DoWork += delegate(object sender, DoWorkEventArgs e)
 {
     using(var mrEvent = e.Argument as ManualResetEvent)
     {
         // some processing...

         mrEvent.WaitOne();
     }
     // broadcast an event
 };
 bgWkr.RunWorkerAsync(mre);

 // some other processing...

 // hook into the same event
 mre.Set();

但是請注意,這引起了一個有趣的問題,即如果主線程在“其他處理...”中引發異常,將會發生什么—永遠不會達到對mre.Set()的調用,而工作線程將永遠不會出口。 你可能想要做的mre.Set()finally

 var mre = new ManualResetEvent(false);
 try {
     var bgWkr = new BackgroundWorker();
     bgWkr.DoWork += delegate(object sender, DoWorkEventArgs e)
     {
         using(var mrEvent = e.Argument as ManualResetEvent)
         {
             // some processing...

             mrEvent.WaitOne();
         }
         // broadcast an event
     };
     bgWkr.RunWorkerAsync(mre);

     // some other processing...
 }
 finally {
    // hook into the same event
    mre.Set();
 }

為了回應我的評論(而不是提出問題的答案),我創建了一個類,一旦完成它,便關閉了ManualResetEvent,而無需跟蹤最后一個線程何時使用完它。 感謝Marc Gravell提出的在WaitOne完成后將其關閉的想法。 我在這里向其他人公開。

PS我只能使用.NET 3.5。因此,為什么不使用ManualResetEventSlim。

干杯,

肖恩

public class OneTimeManualResetEvent
{
    private ManualResetEvent _mre;
    private volatile bool _closed;
    private readonly object _locksmith = new object();

    public OneTimeManualResetEvent()
    {
        _mre = new ManualResetEvent(false);
        _closed = false;
    }

    public void WaitThenClose()
    {
        if (!_closed)
        {
            _mre.WaitOne();
            if (!_closed)
            {
                lock (_locksmith)
                {
                    Close();
                }
            }
        }
    }

    public void Set()
    {
        if (!_closed)
            _mre.Set();
    }

    private void Close()
    {
        if (!_closed)
        {
            _mre.Close();
            _closed = true;
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM