我正在编写一个GUI应用程序。
应用程序在其生命周期内打开多个线程。其中一个线程正在处理可能来自其他应用程序的事件,因此它会在一段时间(真实)循环中等待从未被终止的事件。
用户可以在任何时候关闭应用程序。我想关闭主应用程序打开的所有线程。
我正在使用Process.GetCurrentProcess()。Kill();目前处理这个问题。
这是一个很好的解决方案吗?如果没有,为什么以及什么是处理这个问题的正确方法,如何关闭主应用程序打开的所有线程?
答案 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)
一般来说,我这样做是:
把这一切放在一起,你得到:
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线程)。