包含线程的类的IDisposable实现

时间:2010-09-28 12:27:23

标签: c# .net multithreading

早上好! 我们假设我们有以下类:

class MultithreadOperation : IDisposable
{
    private IList<Thread> operationThreads;

    public void StartOperation()
    {
          // Initialize and start threads and put them to operationThreads
    }

    public void StopOperation()
    {
          // Aborts each thread.
    }

    public void Dispose()
    {
          Dispose(true);
          GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
         if (!disposed)
         {
              disposed = true;
              if (disposing)
              {
                    // Release managed resources.
                    #1:
                    StopOperation();
              }
              // Release unmanaged resources.
              #2:
              StopOperation();
         }
    }

    ~MultithreadOperation()
    {
         Dispose(false);
    }
}

实际上,如果放置实例,我需要停止所有线程。此外,如果实例被垃圾收集,我需要停止所有线程(否则,线程将仍然存活,这对我不利)。 当然,在位置#1中调用StopOperation()方法是完全合法的。

我想知道如果我们在第2位调用StopOperation()会有任何陷阱吗? 据我所知,当~MultithreadOperation()执行时,线程列表可能已被垃圾收集。另外,我已经看到很多建议,以避免任何代码引用Finalize实现中的托管资源,尤其是实例字段。

此外,听到有关此问题的不同方法会很有趣。 谢谢!

2 个答案:

答案 0 :(得分:5)

这完全不合适。你的类的用户不熟悉细节会假设调用Dispose()会做一些无辜的事情。有点清理,没什么特别的。她将期望程序处于完全不可预测的状态,线程会在随机位置中断该状态而无法恢复状态。

在终结器线程上也是完整的redrum,如果线程不处于可警告状态,则中止线程可能需要很多秒。 CLR将在2秒后中止终结器线程并终止程序,根本不提供有关程序崩溃的真正原因的诊断。

答案 1 :(得分:2)

在位置#2拨打StopOperation是不安全的,因为:

  • 可能已经收集了List个帖子或Thread个实例本身。
  • 目前尚不清楚你的StopOperation是否阻塞,直到所有线程都确认了停止信号,但如果确实存在,那么它可能会挂断终结器线程。

另一点是,在这种情况下,您不应该中止线程(通过Thread.Abort)。原因是中止在非确定性点向目标线程中注入带外(异步)异常。因为这些注入点是随机的(并且通常是非直观的),所以它可以在应用程序域甚至整个过程中破坏状态(通过在写入或其他更复杂操作的中间早期挽救)。

处理此问题的最佳方法是执行以下操作:

  • 让线程轮询或收听停止信号。您可以查看my answer here以获取有关如何执行此操作的提示。
  • StopOperation表示您选择的停止机制。通常,您希望等到所有线程都收到信号并自行关闭。您可以致电Thread.Join
  • 等待一个主题
  • 仅在位置#1的StopOperation方法中调用Dispose

现在我们如何处理那些不好玩的调用者并避免调用Dispose?这有点棘手。我看到了处理这个问题的两种一般模式。

  • 让终结器切换停止标志。该标志必须是值类型,否则将受到垃圾回收。每个线程必须定期轮询此标志。这意味着你不应该执行任何无限期等待的阻塞调用。请记住,您无法调用Thread.Interrupt来戳戳线程,因为这需要引用Thread实例。
  • 维护静态集合变量中的活动线程列表,这样您可以在终结器中安全地枚举它们。但是,我必须承认,在应用程序域卸载期间(即应用程序关闭),关于静态引用的收集规则,我有点生疏。所以我不确定它是完全安全。