何时应该销毁ManualResetEvent?

5
我正在使用一个应用程序,该应用程序使用ManualResetEvent同步线程。FxCop告诉我要处理这些对象。我找到了以下讨论,告诉我同样的事情:Do I need to Dispose() or Close() an EventWaitHandle? 但我不知道何时处理ManualResetEvent实例。下面的简化代码演示了问题:
private void btn_Click(object sender, EventArgs e)
{
    var mre = new ManualResetEvent(false);
    new Thread(() => this.SetEvent(mre)).Start();
    for (int i = 0; i < 10; ++i)
    {
        new Thread(() => this.WaitFor(mre)).Start();
    }
}

private void SetEvent(ManualResetEvent manualResetEvent)
{
    Thread.Sleep(10000);
    manualResetEvent.Set();
}

private void WaitFor(ManualResetEvent manualResetEvent)
{
    manualResetEvent.WaitOne();
}

问题在于存在多个ManualResetEvent实例,且多个线程正在等待每个实例。
如果我将实例记忆在列表中,我不知道何时应该处理它。在WaitOne()调用后处理它会导致多次处理并且可能会在其他线程仍在等待时被处理。
创建事件的线程没有任何对它的引用。设置线程不应该处理它,因为还有其他线程正在等待这个MRE。正如前面提到的,每个等待线程都无法处理它。
所以问题是:何时应该处理此ManualResetEvent?

1
代码毫无意义,而 MRE 的实例只有 一个。在方法结束时处理它是可以的。 - Hans Passant
1
不,我不这么认为。创建MRE的代码是一个按钮单击事件处理程序。您可以多次单击它以创建多个MRE。上面的代码已经简化了。在此方法结束时处置MRE将无法正常工作,因为有11个线程在方法完成后使用此MRE。设置器线程等待10秒钟,另外10个线程正在等待此MRE。 - Sebastian Schumann
1个回答

5
ManualResetEvent应该在您不再需要它时进行处理。您真正的问题是,“我如何知道我不再需要它?”通常情况下,当线程完成时会有某些通知,并且您可以在线程上调用Join。或者线程设置某个事件来指示它已经完成。如果您有多个线程,则它们都可以发出信号CountdownEvent。还有其他几种管理线程通知的方法。
重点是,如果要分配资源,则由您负责确保正确处理这些资源。在您上面的代码中,您没有任何方法来跟踪哪些线程正在执行或哪些线程与哪个ManualResetEvent相关联。如果您想确保MRE被正确处理,则需要跟踪它,不仅跟踪MRE还跟踪哪些线程正在使用它,哪些线程已完成其工作,并在所有线程完成时得到通知,以便您可以处理一切。
在您特定的情况下,如果您实在必须以这种方式使用MRE,则我可能会创建一个包含对线程和MRE的引用以及线程完成时线程发出的CountdownEvent的数据结构。例如:
class WorkUnit
{
    public List<Thread> Threads;
    public ManualResetEvent MRE;
    public CountdownEvent CE;
}

现在,当一个线程结束时,它会执行以下操作:
workUnit.CE.Signal();

您的程序的其它部分(可能是主线程)会定期检查工作单元列表。对于该列表中的每个项目,它会执行以下操作:

if (workUnit.CE.WaitOne(0))
{
    foreach (Thread t in workUnit.Threads)
    {
        t.Join();
    }
    // dispose the MRE and the CE
    // and remove the work unit from the list
}

是的,这是很多工作。最好能够构建您的程序,使您不必做这种事情。


谢谢Jim。唯一的问题是我不知道有多少线程会等待MRE,所以我无法初始化CountDownEvent。但你说得对,我应该考虑一个设计,知道何时不再需要MRE。目前没有线程等待工作线程的结束,因为它们计算一些值并在控件中显示结果。之后任务(哦,是的,我正在使用任务而不是线程,但我认为问题仍然相同,因为任务可以使用ContinueWith设置CountDownEvent)结束,没有代码明确等待它。 - Sebastian Schumann
我找到了我的问题的解决方案。setter-thread 只在一个方法中创建并启动,该方法返回 MRE。调用此方法的人知道 MRE 并将其传播到多个线程,但它知道接收者。我能够将 MRE 包装到处理程序对象中,以管理 MRE 接收器并等待它们结束。之后可以释放 MRE。感谢您的帮助。 - Sebastian Schumann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接