[英]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
是幾乎肯定已mrEvent
的ManualResetEvent
。 從技術上講,工作線程可能已經准備就緒,並且工作線程執行“某些處理...”的速度比主線程執行“其他處理...”的速度快,但是:我不會依賴在上面。 因此,在大多數情況下: 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.