猿问

可以像中止一个Thread(Thread.Abort方法)一样中止一个Task吗?

可以像中止一个Thread(Thread.Abort方法)一样中止一个Task吗?

我们可以中止这样的线程:

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

但是我可以以同样的方式中止任务(在.Net 4.0中)而不是取消机制。我想立即杀死任务。


慕的地8271018
浏览 1227回答 3
3回答

人到中年有点甜

不使用线程中止的指导是有争议的。我认为仍有一席之地,但在特殊情况下。但是,您应该始终尝试围绕它进行设计并将其视为最后的手段。例;您有一个简单的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个要处理的项目。用户单击进程按钮并开始循环,我们在iListOfItems集合中对1000个项目执行“最多”20个线程。每次迭代都在自己的线程上执行。每个线程在Parallel.ForEach创建时都将使用前台线程。这意味着无论主应用程序关闭,app域都将保持活动状态,直到所有线程都完成。但是,用户需要关闭应用程序,因为他们关闭了表单。这20个线程将继续执行,直到处理完所有1000个项目。在这种情况下,这并不理想,因为应用程序不会像用户期望的那样退出并将继续在幕后运行,这可以通过查看任务管理器来看出。假设用户再次尝试重建应用程序(VS 2010),它报告exe已锁定,那么他们必须进入任务管理器才能终止它或者等到所有1000个项目都被处理完毕。我不会责怪你说,但当然!我应该使用CancellationTokenSource对象取消这些线程并调用Cancel ...但是从.net 4.0开始存在一些问题。首先,这仍然不会导致线程中止,这将提供中止异常,然后是线程终止,因此app域将需要等待线程正常完成,这意味着等待最后一次阻塞调用,这将是最终调用的最后一次运行迭代(线程)po.CancellationToken.ThrowIfCancellationRequested。在示例中,这意味着app域仍然可以保持活动长达2分钟,即使表单已关闭并取消调用。注意,CancellationTokenSource上的Calling Cancel不会在处理线程上抛出异常,这确实会中断阻塞调用,类似于线程中止并停止执行。当所有其他线程(并发迭代)最终完成并返回时,一个异常被缓存,在启动线程(声明循环的地方)中抛出异常。我选择不在 CancellationTokenSource对象上使用Cancel选项。这是浪费的,并且可以说是违反了众所周知的控制异常代码流的反模式。相反,实现一个简单的线程安全属性,即Bool stopExecuting,可以说是“更好”。然后在循环中检查stopExecuting的值,如果外部影响将值设置为true,我们可以采用备用路径优雅地关闭。因为我们不应该调用cancel,所以这排除了检查CancellationTokenSource.IsCancellationRequested,否则这将是另一个选项。如果条件在循环中适当,则类似以下内容;if(loopState.ShouldExitCurrentIteration || loopState.IsExceptional || stopExecuting){loopState.Stop(); 返回;}迭代现在将以“受控”的方式退出,并终止进一步的迭代,但正如我所说,这对我们必须等待长时间运行和阻塞每次迭代中的调用的问题几乎没有作用(并行循环线程),因为这些必须在每个线程可以选择检查它是否应该停止之前完成。总之,当用户关闭表单时,将通过stopExecuting通知20个线程停止,但它们只会在完成执行长时间运行的函数调用后停止。我们无法做任何关于应用程序域将始终保持活动并且仅在所有前台线程完成时释放的事实。这意味着等待在循环内完成的任何阻塞调用都会有延迟。只有真正的线程中止才能中断阻塞调用,并且您必须尽可能地避免系统处于不稳定/未定义状态,而不必担心中止线程的异常处理程序。这是否适合程序员决定,根据他们选择维护的资源句柄以及在线程的最终块中关闭它们是多么容易。您可以使用令牌注册以取消作为半解决方案终止,即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         }     }});但这仍然会在声明线程中导致异常。考虑到所有事情,使用parallel.loop构造的中断阻塞调用可能是选项的一种方法,避免使用更复杂的库部分。但是为什么没有选择取消并避免在声明方法中抛出异常会让我觉得可能是疏忽。

蓝山帝景

但是我可以以同样的方式中止任务(在.Net 4.0中)而不是取消机制。我想立即杀死任务。其他的回答者告诉你不要这样做。但是,是的,你可以做到。您可以提供Thread.Abort()由Task的取消机制调用的委托。以下是您可以配置的方法:class&nbsp;HardAborter{ &nbsp;&nbsp;public&nbsp;bool&nbsp;WasAborted&nbsp;{&nbsp;get;&nbsp;private&nbsp;set;&nbsp;} &nbsp;&nbsp;private&nbsp;CancellationTokenSource&nbsp;Canceller&nbsp;{&nbsp;get;&nbsp;set;&nbsp;} &nbsp;&nbsp;private&nbsp;Task<object>&nbsp;Worker&nbsp;{&nbsp;get;&nbsp;set;&nbsp;} &nbsp;&nbsp;public&nbsp;void&nbsp;Start(Func<object>&nbsp;DoFunc) &nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;WasAborted&nbsp;=&nbsp;false; &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;start&nbsp;a&nbsp;task&nbsp;with&nbsp;a&nbsp;means&nbsp;to&nbsp;do&nbsp;a&nbsp;hard&nbsp;abort&nbsp;(unsafe!) &nbsp;&nbsp;&nbsp;&nbsp;Canceller&nbsp;=&nbsp;new&nbsp;CancellationTokenSource(); &nbsp;&nbsp;&nbsp;&nbsp;Worker&nbsp;=&nbsp;Task.Factory.StartNew(()&nbsp;=>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;specify&nbsp;this&nbsp;thread's&nbsp;Abort()&nbsp;as&nbsp;the&nbsp;cancel&nbsp;delegate &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;using&nbsp;(Canceller.Token.Register(Thread.CurrentThread.Abort)) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;DoFunc(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;catch&nbsp;(ThreadAbortException) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WasAborted&nbsp;=&nbsp;true; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;false; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;},&nbsp;Canceller.Token); &nbsp;&nbsp;} &nbsp;&nbsp;public&nbsp;void&nbsp;Abort() &nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;Canceller.Cancel(); &nbsp;&nbsp;}}免责声明:不要这样做。以下是不应该做的事情的示例:&nbsp;var&nbsp;doNotDoThis&nbsp;=&nbsp;new&nbsp;HardAborter(); &nbsp;//&nbsp;start&nbsp;a&nbsp;thread&nbsp;writing&nbsp;to&nbsp;the&nbsp;console &nbsp;doNotDoThis.Start(()&nbsp;=> &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(true) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.Sleep(100); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Console.Write("."); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;null; &nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;//&nbsp;wait&nbsp;a&nbsp;second&nbsp;to&nbsp;see&nbsp;some&nbsp;output&nbsp;and&nbsp;show&nbsp;the&nbsp;WasAborted&nbsp;value&nbsp;as&nbsp;false &nbsp;Thread.Sleep(1000); &nbsp;Console.WriteLine("WasAborted:&nbsp;"&nbsp;+&nbsp;doNotDoThis.WasAborted); &nbsp;//&nbsp;wait&nbsp;another&nbsp;second,&nbsp;abort,&nbsp;and&nbsp;print&nbsp;the&nbsp;time &nbsp;Thread.Sleep(1000); &nbsp;doNotDoThis.Abort(); &nbsp;Console.WriteLine("Abort&nbsp;triggered&nbsp;at&nbsp;"&nbsp;+&nbsp;DateTime.Now); &nbsp;//&nbsp;wait&nbsp;until&nbsp;the&nbsp;abort&nbsp;finishes&nbsp;and&nbsp;print&nbsp;the&nbsp;time &nbsp;while&nbsp;(!doNotDoThis.WasAborted)&nbsp;{&nbsp;Thread.CurrentThread.Join(0);&nbsp;} &nbsp;Console.WriteLine("WasAborted:&nbsp;"&nbsp;+&nbsp;doNotDoThis.WasAborted&nbsp;+&nbsp;"&nbsp;at&nbsp;"&nbsp;+&nbsp;DateTime.Now); &nbsp;Console.ReadKey();
随时随地看视频慕课网APP
我要回答