c# - 使用 Dispose() 或终结器来清理托管线程?

标签 c# multithreading idisposable finalizer

假设我在 C++0x 中有一个消息泵类,如下所示(注意,SynchronizedQueue 是函数 的队列,当您在队列上调用 receive() 并且它是空的时,它会阻塞调用线程直到有一个项目要返回):

class MessagePump
{
 private:
    bool done_;
    Thread* thread_;
    SynchronizedQueue queue_;

    void Run()
    {
        while (!done)
        {
            function<void()> msg = queue_.receive();
            msg();
        }
    }
 public:
    MessagePump(): 
        done_(false)
    {
        thread_ = new thread ([=] { this->Run(); } ) );
    }

    ~MessagePump()
    {
        Send( [&]{ done = true; } );
        thread_->join();
    }

    void Send (function<void()> msg)
    {
        queue_.send(msg);
    }
};

我已经把这个类转换成了C#,但是我对析构函数中的代码有疑问。根据 IDisposable 模式,我应该只提供一个 Dispose() 方法来释放托管和非托管资源。

我应该将 C++ 析构函数代码放入:

  1. 客户端需要在应用程序退出时调用的自定义 CleanUp() 方法?如果客户忘记了怎么办?
  2. IDisposable 的 Dispose() 方法,以便客户端也可以调用它?但是,如果客户忘记了怎么办?
  3. 在 C# 终结器方法中以便它始终执行?我读到如果您没有任何非托管资源,则不应包含终结器方法,因为它会影响性能。
  4. 无处可去?忽略标记 done_ 标志并让 GC 自然处理它,因为 Thread 对象是托管资源?这样线程会不会被强制中止?

我还发现,如果我不将在构造函数中创建的消息泵线程标记为后台线程,我的 MessagePump 对象将永远不会被 GC 处理,应用程序在退出时就会挂起。这是什么原因?

最佳答案

在高层次上,我只建议使用 .NET 线程池 ( System.Threading.ThreadPool ) 来排队和执行多个工作项,因为这是它的设计目的(假设允许工作项异步执行) .具体来说,查看 QueueUserWorkItem方法。

不过要回答您的问题:

Should I put the C++ destructor code into:

A custom CleanUp() method that the client needs to call when application is exiting? What if the client forgets?

A Dispose() method of IDisposable so that the client can also call it? But again, what if the client forgets?

总是更喜欢实现 IDisposable 而不是自定义 CleanUp 方法(在 BCL 中,一些 Stream 类有一个 Close方法实际上只是 Dispose 的别名)。 IDisposable 模式是使用 C# 进行确定性清理的方法。客户端忘记调用 Dispose 始终是一个问题,但这通常可以通过静态分析工具(例如 FxCop)检测到。

Inside the C# finalizer method so it will always execute? I read that if you do not have any unmanaged resources, you shouldn't include a finalizer method because it hurts performance.

终结器不能保证执行(参见 this 文章),因此正确的程序不能假设它们会执行。性能在这里不会成为问题。我猜您最多会有几个 MessagePump 对象,因此拥有终结器的成本微不足道。

Nowhere? Just ignore marking the done_ flag and just let GC handle it naturally since the Thread object is a managed resource? Will the thread be forcibly aborted in this way?

线程由 CLR 管理,将被适当清理。如果线程从其入口点(此处为 Run)返回,它不会被中止,只会干净地退出。这段代码仍然需要去某个地方,所以我会通过 IDisposable 提供明确的清理。

I have also found out that if I don't mark the message pump thread created inside the constructor as a background thread, my MessagePump object never gets GC'ed and the application just hangs when it exits. What's the reason for this?

.NET 应用程序一直运行到所有前台(非后台)线程终止为止。因此,如果您不将 MessagePump 线程标记为后台线程,它将在您的应用程序运行时保持事件状态。如果某些对象仍然引用您的 MessagePump,则 MessagePump 将永远不会被 GC 处理或最终确定。不过,再次引用上面的文章,您不能假设终结器会一直运行。

关于c# - 使用 Dispose() 或终结器来清理托管线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3723594/

相关文章:

visual-studio-2010 - 处理 HtmlControl

c# - 为什么 IDisposable 立即调用 Dispose()?

c# - .Net 性能周期性?

c# - T[,] 和 T[*,*] 有什么区别?

java - 子线程阻塞java中的父线程

objective-c - 这两种在obj-c中调用主线程的方法有什么不同吗?

c - 在 C 的 POSIX 线程中使用时,recvfrom() 给出错误的文件描述符错误

c# - 如何修复下面的LinQ笛卡尔程序并使之运行?

c# - 任何人都知道如何进行 List<string> 不区分大小写的比较

c# - 使用语句与 IDisposable.Dispose()