如何像中止线程(Thread.Abort方法)一样中止任务?

问题描述 投票:61回答:8

我们可以像这样中止Thread

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

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

c# .net parallel-processing .net-4.0
8个回答
33
投票
  1. 您不应该使用Thread.Abort()
  2. 可以取消任务但不能中止。

Thread.Abort()方法(严重)已弃用。

线程和任务在停止时都应该协同工作,否则,有使系统处于不稳定/不确定状态的风险。

如果确实需要运行一个Process并从外部将其杀死,那么唯一安全的选择是在单独的AppDomain中运行它。


此答案是关于.net 3.5及更早版本的。

此后,线程中止处理得到了改进。通过更改最终阻止工作的方式。

但是Thread.Abort仍然是可疑的解决方案,您应该始终避免使用它。


45
投票

关于不使用中止线程的指导原则是有争议的。我认为除了特殊情况外,还有地方。但是,您应该始终尝试围绕它进行设计,并将其视为最后的选择。

示例;

您有一个简单的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创建时,每个线程都将利用前台线程。这意味着无论主应用程序关闭如何,应用程序域都将保持活动状态,直到所有线程完成。

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

假设用户尝试重新构建该应用程序(VS 2010),它报告该exe文件已被锁定,那么他们将不得不进入任务管理器中将其杀死,或者仅等到所有1000个项目都处理完后再执行。

我不会怪你这么说,但是当然!我应该使用CancellationTokenSource对象取消这些线程并调用Cancel ...,但是从.net 4.0开始,这样做存在一些问题。首先,这仍然永远不会导致线程中止,它会提供中止异常并随后终止线程,因此应用程序域将需要等待线程正常完成,这意味着等待上一次阻塞调用,这将是最后运行并最终调用po.CancellationToken.ThrowIfCancellationRequested的迭代(线程)。在该示例中,这意味着即使关闭并取消了调用表单,应用程序域仍可以保留最多2分钟的生命。

[请注意,在CancellationTokenSource上调用Cancel不会在处理线程上引发异常,这确实会像中断线程一样中断中断调用并停止执行。当所有其他线程(并发迭代)最终完成并返回时,将为要缓存的异常做好准备,该异常会在启动线程(声明循环的位置)中引发。

我选择not

以在CancellationTokenSource对象上使用Cancel选项。这是浪费的,并且可以说违反了通过例外控制代码流的众所周知的反专利。

相反,可以说实现一个简单的线程安全属性(即Bool stopExecuting)是“更好的选择”。然后在循环中,检查stopExecuting的值,如果该值被外部影响设置为true,我们可以采用替代路径来正常关闭。由于我们不应该调用cancel,因此排除了检查CancellationTokenSource.IsCancellationRequested的可能性,否则它是另一种选择。

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

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

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

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

我们对应用程序域将始终保持活动状态并且仅在所有前台线程都已完成时才释放的事实无能为力。这意味着等待循环中完成的所有阻塞调用都会有一个延迟。

只有真正的线程中止才能中断阻塞调用,并且您必须尽最大努力在异常中止的线程异常处理程序中使系统保持不稳定/未定义状态,这是毫无疑问的。对于程序员来说,这是否合适是一个问题,要根据他们选择维护的资源句柄以及在线程的finally块中关闭它们的难易程度来决定。您可以使用令牌进行注册,以通过半替代方法在取消时终止,即

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构造中断中断调用本可以作为选项的一种方法,避免使用库中更晦涩难懂的部分。但是为什么在声明方法中没有取消和避免引发异常的选项,这让我感到可能是疏忽大意。


39
投票

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


7
投票

每个人都(希望)知道终止线程是不好的。问题是当您不拥有要调用的代码时。如果此代码在某些do / while无限循环中运行,它本身在调用某些本机函数,等等。当这种情况发生在您自己的代码终止,停止或Dispose调用中时,可以开始射击坏人(这样您就不会自己成为坏人)。


1
投票

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


-1
投票

您可以通过在您控制的线程上运行该任务并中止该线程来“中止”任务。这会导致任务以ThreadAbortException在故障状态下完成。您可以使用自定义任务计划程序控制线程的创建,如this answer中所述。请注意,caveat about aborting a thread适用。


-2
投票
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();
});

-3
投票

如果您具有Task构造函数,那么我们可以从Task中提取Thread,然后调用thread.abort。

© www.soinside.com 2019 - 2024. All rights reserved.