关闭多线程应用程序

8
我正在尝试为我的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打了勾,因为他最先回答的,从他那里拿走不公平。让我们看看票数说什么吧。我从你们所有人的答案中都得到了有用的东西,所以请投下你们的心意。 - Kiril
线程池线程的默认行为与我建议的相同。如果您不关心您的线程是否完成,我的解决方案是最简单的。显然,其他回答提出了管理线程的好方法,但是您明确声明在计算过程中终止线程是可以的。 - Nate Heinrich
我同意你的看法,Nate ;),我不会取消勾选标记,因为这样是不公平的,而且你给了我最准确、快速和简单的答案... 我希望其他人也能得到应有的认可,所以我希望大家都投票支持所有好的回答。 - Kiril
6个回答

16

如果将线程设置为后台线程,在应用程序关闭时它们会被终止。

myThread.IsBackground = true;

显然,如果你需要在线程关闭之前完成任务,那么这并不是你想要的解决方案。


谢谢Nate,我在创建线程时将它们全部设置为后台线程...这是否意味着我根本不需要中止它们? - Kiril
是的,如果它们被设置为后台线程,关闭应用程序将强制退出这些线程。 - Nate Heinrich

14

1
线程中止异常在 finally 块执行期间被阻止这一点对我来说是新的。感谢您添加这个信息。 - Justin R.
@fatcat1111:确实,没有任何保证中止的线程会因为中止而永远停止。这正是避免使用Thread.Abort的另一个原因;它既危险又不可靠。 - Eric Lippert
同意,绝对是最后的选择。 - Jamie Keeling
1
@Jamie Keeling:不,这正是关键所在:因为它不可靠,所以它不是最后的手段。最后的手段应该是断开主机的所有电源来源。 - jason
我并不是说Thread.Abort()是最后的救命稻草,断电才是终极安全措施。我只是想说,Thread.Abort()应该只在真正必要的情况下使用。 - Jamie Keeling

3

如果在您的关闭程序运行时调用AddThread会怎样?

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

添加一个布尔标志,在Shutdown中仅设置一次以防止此问题发生。

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?
        }
    }

此外,您不应该使用静态成员——因为您已经有了单例实例,所以没有理由这样做。

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


2

1

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

void DieDieDie()
{
  foreach (Thread thread in _thread)
    {
      thread.Abort();
      thread.Join(); // if you need to wait for the thread to die
    }
}
在你的情况下,你可能可以中止所有计算并关闭程序,但如果你需要等待数据库写入操作或需要关闭非托管资源,则必须捕获ThreadAbortException或通知线程以优雅地结束自己。

0
你想要延迟线程取消,这基本上意味着线程自行终止,而不是线程管理器异步取消线程,后者更加模糊和危险。 如果你想比立即终止更优雅地处理线程取消,可以使用由线程外部事件触发的信号处理程序 - 例如由你的线程管理器触发。

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