是否有可能放弃一个任务,如中止一个线程(Thread.Abort方法)?

我们可以放弃这样的线程:

Thread thread = new Thread(SomeMethod); . . . thread.Abort(); 

但是我可以用相同的方式中止任务(在.Net 4.0中)而不是通过取消机制。 我想马上杀掉这个任务

  1. 你不应该使用Thread.Abort()
  2. 任务可以取消,但不能中止。

Thread.Abort()方法(严重)不推荐使用。

线程和任务在停止时应该协作,否则会冒着让系统处于不稳定/未定义状态的风险。

如果你确实需要运行一个进程并从外部杀死它,唯一的安全select就是在一个单独的AppDomain中运行它。

关于不使用线程中止的指导是有争议的。 我认为在特殊情况下仍然有一席之地。 但是,你应该总是尝试围绕它devise,并将其视为最后的手段。

例;

您有一个简单的Windows窗体应用程序连接到阻塞同步Web服务。 其中它在并行循环内的Web服务上执行一个函数。

 CancellationTokenSource cts = new CancellationTokenSource(); ParallelOptions po = new ParallelOptions(); po.CancellationToken = cts.Token; po.MaxDegreeOfParallelism = System.Environment.ProcessorCount; Parallel.ForEach(iListOfItems, po, (item, loopState) => { Thread.Sleep(120000); // pretend web service call }); 

说在这个例子中,阻塞呼叫需要2分钟才能完成。 现在我把我的MaxDegreeOfParallelism设置为ProcessorCount。 iListOfItems中有1000个项目要处理。

用户点击进程button,循环开始,我们有'最多'20个线程执行iListOfItems集合中的1000个项目。 每个迭代在其自己的线程上执行。 每个线程在由Parallel.ForEach创build时都将使用前台线程。 这意味着无论主应用程序closures,应用程序域都将保持活动状态,直到所有线程都完成。

但是,由于某种原因,用户需要closures应用程序,并说他们closures了表单。 这20个线程将继续执行,直到处理完所有1000个项目。 这在这种情况下并不理想,因为应用程序不会像用户期望的那样退出,并且将继续在幕后运行,这可以通过查看任务pipe理器来看到。

假设用户试图重新构build应用程序(VS 2010),它报告exe被locking,然后他们将不得不进入任务pipe理器杀死它,或者等待所有1000个项目被处理。

我不会责怪你说的,当然! 我应该取消这些线程使用CancellationTokenSource对象,并调用取消…但这有一些问题与.net 4.0。 首先,这仍然不会导致线程中止,这会导致终止exception,然后终止线程,所以应用程序域将需要等待线程正常完成,这意味着等待最后的阻塞调用,这将是最后运行的迭代(线程),最终得到调用po.CancellationToken.ThrowIfCancellationRequested 。 在这个例子中,这意味着应用程序域可能仍然活着长达2分钟,即使表单已经closures,取消调用。

请注意,取消CancellationTokenSource的调用不会在处理线程上抛出exception,这确实会中断类似于线程中止的阻塞调用,并停止执行。 当所有其他线程(并发迭代)最终完成并返回时,caching的exception已准备好,exception将在启动线程(声明循环的地方)中抛出。

我select使用CancellationTokenSource对象上的取消选项。 这是浪费的,可以说是违反了众所周知的通过例外来控制代码stream的反模式。

相反,实现一个简单的线程安全属性,即Bool stopExecuting,可以说是“更好”的。 然后在循环中检查stopExecuting的值,如果通过外部影响将该值设置为true,我们可以采取一个备用path来优雅地closures。 由于我们不应该调用取消,这排除检查CancellationTokenSource.IsCancellationRequested否则将是另一种select。

如果条件在循环内是适当的,则类似于以下内容;

if(loopState.ShouldExitCurrentIteration || loopState.IsExceptional || stopExecuting){loopState.Stop(); 返回;}

迭代现在将以“受控”的方式退出,并终止进一步的迭代,但正如我所说的,这对我们不得不等待每次迭代中长时间运行和阻塞调用的问题(并行循环线程),因为这些都必须在每个线程可以到达检查是否应该停止的选项之前完成。

总之,当用户closures表单时,20个线程将通过stopExecuting信号通知停止,但是只有在完成执行长时间运行的函数调用后才会停止。

我们无法做任何事情,即应用程序域将始终保持活动状态,只有在所有前台线程完成后才能释放。 这意味着等待循环内的任何阻塞呼叫完成将会有一个延迟。

只有一个真正的线程中止可以中断阻塞调用,并且你必须减轻离开系统在一个不稳定/未定义的状态最好你可以在中止线程的exception处理程序,毫无疑问。 基于他们select维护哪些资源句柄,以及在线程的最后一个块中closures它们是多么容易,这是否合适是程序员决定的事情。 你可以注册一个令牌终止取消作为一种半解决方法,即

 CancellationTokenSource cts = new CancellationTokenSource(); ParallelOptions po = new ParallelOptions(); po.CancellationToken = cts.Token; po.MaxDegreeOfParallelism = System.Environment.ProcessorCount; Parallel.ForEach(iListOfItems, po, (item, loopState) => { using (cts.Token.Register(Thread.CurrentThread.Abort)) { Try { Thread.Sleep(120000); // pretend web service call } Catch(ThreadAbortException ex) { // log etc. } Finally { // clean up here } } }); 

但是这仍然会导致声明线程出现exception。

所有事情都考虑到了,使用parallel.loop构造的中断阻塞调用可能是选项中的一种方法,避免使用库中更模糊的部分。 但是,为什么没有select取消和避免在声明方法中抛出exception,这是我的一个可能的疏忽。

但是我可以用相同的方式中止任务(在.Net 4.0中)而不是通过取消机制。 我想马上杀掉这个任务

其他答复者已经告诉你不要这样做。 但是,是的,你可以做到。 您可以提供Thread.Abort()作为由任务的取消机制调用的委托。 这里是你如何configuration这个:

 class HardAborter { public bool WasAborted { get; private set; } private CancellationTokenSource Canceller { get; set; } private Task<object> Worker { get; set; } public void Start(Func<object> DoFunc) { WasAborted = false; // start a task with a means to do a hard abort (unsafe!) Canceller = new CancellationTokenSource(); Worker = Task.Factory.StartNew(() => { try { // specify this thread's Abort() as the cancel delegate using (Canceller.Token.Register(Thread.CurrentThread.Abort)) { return DoFunc(); } } catch (ThreadAbortException) { WasAborted = true; return false; } }, Canceller.Token); } public void Abort() { Canceller.Cancel(); } } 

免责声明 :不要这样做。

这是一个不能做的例子:

  var doNotDoThis = new HardAborter(); // start a thread writing to the console doNotDoThis.Start(() => { while (true) { Thread.Sleep(100); Console.Write("."); } return null; }); // wait a second to see some output and show the WasAborted value as false Thread.Sleep(1000); Console.WriteLine("WasAborted: " + doNotDoThis.WasAborted); // wait another second, abort, and print the time Thread.Sleep(1000); doNotDoThis.Abort(); Console.WriteLine("Abort triggered at " + DateTime.Now); // wait until the abort finishes and print the time while (!doNotDoThis.WasAborted) { Thread.CurrentThread.Join(0); } Console.WriteLine("WasAborted: " + doNotDoThis.WasAborted + " at " + DateTime.Now); Console.ReadKey(); 

从示例代码输出

虽然有可能中止一个线程,但实际上这样做几乎总是一个非常糟糕的主意。 放弃一个线程意味着线程没有机会自行清理,使资源不被删除,而事物处于未知状态。

在实践中,如果你放弃了一个线程,你只能在杀死这个进程的时候这样做。 不幸的是,太多人认为ThreadAbort是一种可行的停止和继续的方法,事实并非如此。

由于任务以线程运行,因此可以在它们上调用ThreadAbort,但与通用线程一样,几乎不需要这样做,除非是最后的手段。

 using System; using System.Threading; using System.Threading.Tasks; ... var cts = new CancellationTokenSource(); var task = Task.Run(() => { while (true) { } }); Parallel.Invoke(() => { task.Wait(cts.Token); }, () => { Thread.Sleep(1000); cts.Cancel(); }); 

这是一个简单的片段,用CancellationTokenSource放弃一个永无止境的任务。