【问题标题】:Shutting down a multithreaded application关闭多线程应用程序
【发布时间】:2010-02-22 22:44:37
【问题描述】:

我正在尝试为我的 C# 应用程序编写一个 ThreadManager。我创建了几个线程:
我的文字作者的一个线程。
一个监控一些统计数据的线程。
多个线程来执行大量计算序列(每个内核最多 4 个线程,我在 2x 四核服务器上运行我的应用程序)。

我的应用程序通常一次运行长达 24 小时,因此所有线程都是在开始时创建的,并且它们会在应用程序运行的整个过程中持续存在。

我想在一个地方“注册”我所有的线程,当应用程序关闭时,我只需调用一个方法,它会遍历所有注册的线程并关闭它们。

为此,我设计了以下课程:

public class ThreadManager
{
    private static Object _sync = new Object();
    private static ThreadManager _instance = null;
    private static List<Thread> _threads;
    private ThreadManager()
    {
        _threads = new List<Thread>();
    }

    public static ThreadManager Instance
    {
        get
        {
            lock (_sync)
            {
                if (_instance == null)
                {
                    _instance = new ThreadManager();
                }
            }
            return _instance;
        }
    }

    public void AddThread(Thread t)
    {
        lock (_sync)
        {
            _threads.Add(t);
        }
    }

    public void Shutdown()
    {
        lock (_sync)
        {
            foreach (Thread t in _threads)
            {
                t.Abort(); // does this also abort threads that are currently blocking?
            }
        }
    }
}

我想确保我的所有线程都被终止,以便应用程序可以正确关闭并在某些计算过程中关闭也很好。我应该知道这里有什么吗?考虑到我的情况,这种方法好吗?

【问题讨论】:

  • 哇!很多好的答案......现在很难选择。我把复选标记给了内特,因为他是第一个,把它从他手里拿走是不公平的。让我们看看投票怎么说。我从你所有的答案中得到了一些有用的东西,所以请投票给你。
  • 线程池线程的默认行为和我建议的一样。如果你不在乎你的线程没有完成,我的是最简单的解决方案。显然,其他答案对管理线程有很好的意义,但是您明确指出在计算过程中杀死线程是可以的。
  • 我同意你的看法,Nate ;),我不会取消勾选标记,因为这不公平,你给了我最准确、最快速、最简单的答案……我愿意希望其他人得到公平的评价,所以我希望每个人都为所有好的答案投票。

标签: c# multithreading


【解决方案1】:

如果您将线程设置为后台线程,它们将在应用程序关闭时被杀死。

myThread.IsBackground = true;

显然,如果您需要线程在关闭之前完成,这不是您想要的解决方案。

【讨论】:

  • 谢谢 Nate,我在创建线程时确实将所有线程都设置为后台线程...这是否意味着我根本不需要中止它们?
  • 是的,如果它们设置为后台线程,关闭应用程序将强制线程退出。 msdn.microsoft.com/en-us/library/…
【解决方案2】:

中止线程是你做的当一切都失败了。这是一件危险的事情,您只能作为最后的手段。正确的做法是让你的线程逻辑,让每个工作线程在主线程发出命令关闭自己时快速正确地响应。

巧合的是,这是我本周博客的主题。

http://blogs.msdn.com/ericlippert/archive/2010/02/22/should-i-specify-a-timeout.aspx

【讨论】:

  • 在 finally 块的执行期间线程中止异常被阻止对我来说是新的。感谢您添加此内容。
  • @fatcat1111:确实,没有任何东西可以保证被中止的线程会永远因为中止而停止。避免使用 Thread.Abort 的另一个原因;它既危险不可靠。
  • 同意,绝对是最后的选择。
  • @Jamie Keeling:不,这正是重点:因为它不可靠,所以不是万不得已。最后的办法是断开主机的所有电源。
  • 我并不是说作为最后的手段,断开电源是最终的故障保险。我只是说 Thread.Abort();只有真正需要时才应该真正使用。
【解决方案3】:

如果在 Shutdown 运行时调用 AddThread 会怎样?

当关闭完成时,在 AddThread 中等待的线程将添加一个新线程到集合中。这可能会导致您的应用程序挂起。

添加一个您曾经只在 Shutdown 中设置的 bool 标志以防止这种情况发生。

bool shouldGoAway = false;
public void AddThread(Thread t)
{
    lock (_sync)
    {
        if( ! shouldGoAway )
            _threads.Add(t);
    }
}

public void Shutdown()
{
    lock (_sync)
    {
        shouldGoAway = true;
        foreach (Thread t in _threads)
        {
            t.Abort(); // does this also abort threads that are currently blocking?
        }
    }

您也不应该使用静态成员 - 因为您有 Singleton 实例,所以没有理由这样做。

.Abort() 不会中止在非托管空间中阻塞的线程。因此,如果您这样做,则需要使用其他一些机制。

【讨论】:

    【解决方案4】:

    我知道的唯一具体问题是这个:http://www.bluebytesoftware.com/blog/2007/01/30/MonitorEnterThreadAbortsAndOrphanedLocks.aspx

    但我会避免使用这样的设计。您可以强制每个线程定期检查某个标志是否该关闭,并在关闭时设置该标志并等待所有线程完成(使用 Join())。这样感觉更像是受控关机。

    【讨论】:

      【解决方案5】:

      如果你不关心工作线程的状态,那么你可以循环 _thread 并中止:

      void DieDieDie()
      {
        foreach (Thread thread in _thread)
          {
            thread.Abort();
            thread.Join(); // if you need to wait for the thread to die
          }
      }
      

      在您的情况下,您可能只是将它们全部中止并关闭,因为它们只是在进行计算。但是,如果您需要等待数据库写入操作或需要关闭非托管资源,那么您需要捕获 ThreadAbortException 或发出信号让线程优雅地杀死自己。

      【讨论】:

        【解决方案6】:

        您想要延迟线程取消,这基本上意味着线程会自行终止,而不是线程管理器异步取消线程,后者定义不明确且危险得多。

        您想比立即终止更优雅地处理线程取消,您可以使用由线程外事件触发的信号处理程序——也许是由您的线程管理器。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-01-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-05
          相关资源
          最近更新 更多