2

考虑以下代码:

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 块。我会假设离开 using 块时 mre 会被关闭(假设它会被处理),这至少会引发异常。这是一个安全的假设吗?

此示例可能不是使用 ManualResetEvent 的最佳示例,但它旨在说明我们在 using 块内访问匿名方法内的 IDisposable 对象并在退出 using 块后调用匿名方法的情况。是否有某种机制可以保留一次性物品?我不这么认为,但想确认一下为什么(如果有某种伏都教在起作用)或者为什么不。

干杯,

4

2 回答 2

1

是的,这段代码是错误的——结果并没有真正定义,但它在 处抛出异常是非常合理的mrEvent.WaitOne(),因为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()在 a中执行以下操作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();
 }
于 2013-09-13T08:45:17.277 回答
0

为了回应我的评论(而不是提出问题的答案),我创建了一个类来关闭 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;
        }
    }
}
于 2013-09-13T09:32:25.837 回答