如何正确关闭已创建多个线程的C#应用​​程序?

时间:2010-10-15 18:08:27

标签: c# .net winforms multithreading

我正在编写一个GUI应用程序。

应用程序在其生命周期内打开多个线程。其中一个线程正在处理可能来自其他应用程序的事件,因此它会在一段时间(真实)循环中等待从未被终止的事件。

用户可以在任何时候关闭应用程序。我想关闭主应用程序打开的所有线程。

我正在使用Process.GetCurrentProcess()。Kill();目前处理这个问题。

这是一个很好的解决方案吗?如果没有,为什么以及什么是处理这个问题的正确方法,如何关闭主应用程序打开的所有线程?

8 个答案:

答案 0 :(得分:23)

如果您将新线程创建为后台线程(通过在启动它们之前设置IsBackground),它们将在主线程(应用程序线程)终止时自动停止。

(来自MSDN):

  

线程是后台线程或前台线程。后台线程与前台线程相同,除了后台线程不会阻止进程终止。一旦属于进程的所有前台线程终止,公共语言运行库就结束该进程。任何剩余的后台线程都会停止并且不会完成。

答案 1 :(得分:2)

一旦你有线程等待某些事件,只需再添加一个事件,触发时将指示线程终止。

如果您不需要为其他线程提供某些正常关闭的方法,您可以将它们切换到“后台线程”模式以确保自动终止 - see MSDN对这个主题的深入讨论。

答案 2 :(得分:1)

有很多方法可以解决这个问题,但理想情况下,你希望你的线程能够自己正常退出,而不仅仅是杀死进程。

你可以做一些非常简单的事情:

public class ThreadSignal
{
   public bool Stop { get; set; }
}

然后在你的线程循环中,执行:

public void DoWork(object state) 
{
   ThreadSignal signal = (ThreadSignal)state;
   while(!signal.Stop)
   {
      // Do work here    
   }
}

然后,当您准备停止时,请将ThreadSignal.Stop设置为true。这是一个非常简单的例子,但它为您提供了一个起点。

答案 3 :(得分:1)

您应该在循环中等待ManualResetEvent(或AutoResetEvent)。 然后在关闭时将成员变量设置为true:

public class MyForm : Form
{
    private AutoResetEvent _workTrigger = new AutoResetEvent();
    private bool _shuttingDown = false;
    private Thread _thread;

    public void Form_Initialize()
    {
        _thread = new Thread(MyThreadMethod);
        _thread.Start();
    }

    public static void MyThreadMethod(object State)
    {
        while (!_shuttingDown)
        {
            //wait for jobs.
            _workTrigger.WaitOne(); //can add a timeout as parameter.

            //do some work here

        }

    }


    public void Form_Closing(object source, EventArgs e)
    {
       _shuttingDown = true;
       _workTrigger.Set();

       //wait for it to exit. You could use the timeout
       //parameter and a loop to not block the UI
       _thread.Join();  
    }
}

答案 4 :(得分:0)

正如您所提到的那样,它是一个GUI应用程序,因此负责消息循环的主线程负责警告用户想要退出程序的无限(while(true))循环。我建议将true替换为另一个boolean,以表示用户已关闭窗口,如下所示:while(windowIsOpen)并在卸载表单时将其设置为false。

答案 5 :(得分:0)

不要丢失应用程序周围的线程 - keep'em某处(List<Thread>会很好)。然后,当时间正确(关闭时间)通知每个人它应该完成它正在做的事情并退出。

然后,.Join()所有这些,然后允许应用程序退出。

永远不要去'ThreadAbort'领域,这是潜伏在那里的力量的黑暗面。

答案 6 :(得分:0)

一般来说,我这样做是:

  • 创建一个封装此行为的类(例如,在后台处理传入的消息
  • 让类继承自IDisposable。调用Dispose()时,设置名为_disposed
  • 的私有变量
  • 在我的类构造函数中创建我的专用线程。
  • 拥有一个名为_workToDo的私有AutoResetEvent。您的后台线程将等待此事件,并且仅在发出此事件信号时执行工作循环。
  • 使用公共方法将消息发送给后台工作人员,然后设置_workToDo以告知后台线程完成工作。

把这一切放在一起,你得到:

public class BackgroundProcessor : IDisposed
{
  private Thread _backgroundThread;
  private bool _disposed;
  private AutoResetEvent _workToDo = new AutoResetEvent(false);
  // where T is a class with the set of parameters for your background work
  private Queue<T> _workQueue = Queue.Synchronized(new Queue<T>);

  public BackgroundProcessor()
  {
    _backgroundThread = new Thread(DoBackgroundWork);
    _backgroundThread.Start();
  }

  public void Dispose()
  {
    _disposed = true;

    // Wait 5 seconds for the processing of any previously submitted work to finish.
    // This gives you a clean exit.  May want to check return value for timeout and log
    // a warning if pending background work was not completed in time.
    // If you're not sure what you want to do yet, a Debug.Assert is a great place to
    // start because it will let you know if you do or don't go over time in general
    // in your debug builds.
    // Do *not* Join() and wait infinitely.  This is a great way to introduce shutdown
    // hangs into your app where your UI disappears but your process hangs around
    // invisibly forever.  Nasty problem to debug later...
    Debug.Assert(_backgroundThread.Join(5000)); 
  }

  // Called by your 'other application'
  public void GiveMeWorkToDo(T workParameters)
  {
    _workQueue.Enqueue(workParameters);
    _workToDo.Set();
  }

  private void DoBackgroundWork()
  {
    while (!_disposed)
    {
      // 500 ms timeout to WaitOne allows your Dispose event to be detected if there is
      // No work being submitted.  This is a fancier version of a Thread.Sleep(500)
      // loop.  This is better because you will immediately start work when a new
      // message is posted instead of waiting for the current Sleep statement to time
      // out first.
      _workToDo.WaitOne(500);

      // It's possible multiple sets of work accumulated or that the previous loop picked up the work and there's none left.  This is a thread safe way of handling this.
      T workParamters = _workQueue.Count > 0 ? workParameters = _workQueue.Dequeue() : null;
      do
      {
        DoSomething(workParameters);

        workParameters = _workQueue.Count > 0 ? workParameters = _workQueue.Dequeue() : null;
      } while (workParameters != null)
    }
  }
}

答案 7 :(得分:0)

考虑使用BackGroundWorker类。由于它使用的是线程池(通过BeginInvoke()),因此您将获得后台线程。作为奖励,您可以获得方便的进度报告,取消和完成回调(已经编组到UI线程)。