【问题标题】:When should a ManualResetEvent be disposed?何时应处置 ManualResetEvent?
【发布时间】:2013-08-29 14:32:14
【问题描述】:

我正在使用一个使用 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() 调用之后释放它会释放它多次,并且可能会在其他线程仍在等待时释放它。

创建事件的线程没有对它的任何引用。 setter-thread 不应该释放它,因为还有其他线程在等待这个 MRE。每个等待的线程都不能像前面提到的那样处理它。

所以问题是:这个 ManualResetEvent 应该在什么时候被释放?

【问题讨论】:

  • 代码是无意义的,只有 一个 的 MRE 实例。在方法结束时处理它就可以了。
  • 不,我不这么认为。创建 MRE 的代码是一个按钮单击事件处理程序。您可以多次单击它来创建多个 MRE。上面的代码被简化了。在此方法结束时处理 MRE 将不起作用,因为在方法完成后有 11 个线程正在使用此 MRE。 setter-thread 等待 10 秒,其他 10 个线程正在等待这个 MRE。

标签: c# .net multithreading idisposable manualresetevent


【解决方案1】:

当您不再需要 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
}

是的,工作量很大。如果你可以构建你的程序,这样你就不必做这种事情,这可能是最好的。

【讨论】:

  • 谢谢吉姆。唯一的问题是我不知道有多少线程将等待 MRE,这就是我无法初始化 CountDownEvent 的原因。但是你说得对,我应该考虑一个知道何时不再需要 MRE 的设计。目前没有线程等待工作线程结束,因为它们计算一些值并在控件中显示结果。之后任务(哦,是的,我使用的是任务而不是线程,但我认为问题仍然存在,因为任务可以使用 ContinueWith 设置 CountDownEvent)结束并且没有明确等待它的代码。
  • 我为我的案例找到了解决方案。 setter-thread 仅在一种返回 MRE 的方法中创建和启动。此方法的调用者知道 MRE 并将其传播到多个线程,但它知道接收者。我能够将 MRE 包装到管理 MRE 接收器的处理程序对象中并等待结束。之后,可以释放 MRE。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多