相当于Thread.Abort的dotnet核心

No1*_*ver 9 c# multithreading .net-core

背景

我有一个Service抽象。每个服务都有它自己的WorkItem。WorkItem能够以一些数据开始。该服务限制了的执行时间WorkItem。假设一个工作项最多可能需要60秒。在此之后,Service应该杀死它。

这段代码是从Standard .NET Framework迁移而来的,我创建了一个Thread运行该Start(model)方法的对象。然后代码是这样的:

Thread t = new Thread(workItem.Start, model);
t.start();
if (!t.Join(TimeSpan.FromSeconds(60)))
    t.Abort();
Run Code Online (Sandbox Code Playgroud)

Thread.Abort被注入了正在运行的线程,从而导致它立即停止异常。

现在,当您调用以下消息时,我将代码移到了dotnet core-您可能知道。Thread.Abort()

System.PlatformNotSupportedException: Thread abort is not supported on this platform.
   at System.Threading.Thread.Abort()
   at ...
Run Code Online (Sandbox Code Playgroud)

目标

我想将的执行时间限制WorkItem为特定的时间。请注意,如果您像这样运行代码行,则此限制也应起作用:

Thread.sleep(61000); // 61 seconds. should be stop after 60 seconds.
Run Code Online (Sandbox Code Playgroud)

进展

在dotnet核心世界中,似乎要使用Task相关的解决方案。因此,我想使用CancellationToken。但是,似乎无法观看“意外事件”并立即停止。我看到的示例使用while (!canceled)循环,不能停止长时间的操作(例如Thread.Sleep(1000000)

怎么做对?

更新资料

我编写了以下示例代码:

public static bool ExecuteWithTimeLimit(TimeSpan timeSpan, Action codeBlock)
{
    try
    {
        Task task = Task.Factory.StartNew(() => codeBlock());
        if (!task.Wait(timeSpan))
        {
            // ABORT HERE!
            Console.WriteLine("Time exceeded. Aborted!");
        }
        return task.IsCompleted;
    }
    catch (AggregateException ae)
    {
        throw ae.InnerExceptions[0];
    }
}
Run Code Online (Sandbox Code Playgroud)

和这个Main文件:

public static void Main(string[] args)
{
    bool Completed = ExecuteWithTimeLimit(TimeSpan.FromMilliseconds(2000), () =>
    {
        Console.WriteLine("start");
        Thread.Sleep(3000);
        Console.WriteLine("end");
    });

    Console.WriteLine($"Completed={Completed}");
    Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)

预期:“结束”不会打印到屏幕上。实际:“结束”打印。有没有其他办法可以杀死一个Task

M K*_*aei 13

使用线程。中断();而不是 Abort 方法。

  • 这确实需要一个解释。 (3认同)
  • 这是完全没用的。您中断处于睡眠状态的线程。如果它处于睡眠状态 - 开发人员可以使用很多解决方案。真正的问题(当您应该使用 Abort 时)是当线程正在等待永远不会返回的阻塞调用(如 I/O)时。 (2认同)

Gyö*_*zeg 10

在不中止的情况下,唯一的解决方案是足够频繁地轮询取消请求,因此在while (!canceled)您提到的所有解决方案之后。

我看到的例子是使用while (!canceled)循环,它不能停止长时间的操作(比如Thread.Sleep(1000000).

这只是部分正确。例如,这可以像这样重写以响应:

 var timeout = TimeSpan.FromSeconds(60);
 var stopwatch = new Stopwatch();
 stopwatch.Start();

 while (!cancelToken.IsCancellationRequested
  && stopwatch.ElapsedMilliseconds < timeout)
{
    Thread.Sleep(10);
}
Run Code Online (Sandbox Code Playgroud)

当然,并不是每个任务都可以像这样轻松地重新编写以轮询取消。如果您处于深度调用链中,检查每个级别的取消可能会很痛苦。出于这个原因,您也可以使用该CancellationToken.ThrowIfCancellationRequested方法,OperationCanceledException如果有取消请求,它将抛出一个。我通常不会只为自己抛出异常并将其用于控制​​流,但取消是可以证明其合理性的领域之一。

与以下解决方案相比,此解决方案当然有一些限制Abort

  • 您将无法取消不支持取消且无法重构的 3rd 方例程
  • OperationCanceledException可以很容易地吞咽,而ThreadAbortException总是在月底再次上调catch块所以第三部分库可以通过即使包含常规catch块一个很好的机会被中止。

更新:

如果您足够自信/绝望,您可以使用通过反射ThreadEx.Abort调用 的方法Thread.AbortInternal。尽管不能保证它会是 .NET Core 中的长期解决方案。

虽然我不完全同意Thread.Abort过时,因为它是关闭您没有影响的例程的好最后机会工具,但我也站在一边,必须不惜一切代价避免堕胎,因为它可以有讨厌的副作用。如果您是整个代码库的作者,则总是可以避免的。

更新 2:

似乎AbortInternal从那时起就被删除了。至少当前的 .NET Core 源不包含这样的方法。

  • dotnet core 2.1 中似乎没有 Thread.InternalAbort() 。还有其他选择吗? (2认同)

Sie*_*sma 9

您可以使用 Thread.Interrupt(),这会在工作线程中引发 ThreadInterruptedException()。您可以使用 try catch 捕获异常,然后安全地将线程与主线程连接起来以清理工作线程。这看起来像这样:

Thread t = new Thread(workItem.Start, model);
t.Start();

// do other stuff or wait

t.Interrupt();
t.Join();
Run Code Online (Sandbox Code Playgroud)

而工作线程的功能是这样的:

try
{
   // stuff the worker thread needs to do
}
catch (Exception e)
{
   // go in here when interrupted
}
Run Code Online (Sandbox Code Playgroud)

然后等待可以这样实现

Thread t = new Thread(workItem.Start, model);
t.Start();
if (!t.Join(TimeSpan.FromSeconds(60)))
{
    t.Interrupt();
    t.Join();
}
Run Code Online (Sandbox Code Playgroud)

这是一种(关闭)终止线程的方法,但使用 CancelationTokens 来实现更干净。我在这里说的是,线程在被操作系统或其他块阻止之前不会被中断。因此,如果线程从不阻塞,则永远不会抛出异常,因此线程可能会完成而不会被中断。

  • 注意:线程**在阻塞之前不会被中断**这一事实应该是答案的一部分(不要隐藏在评论中)。 (4认同)
  • 对于取消标记也可以这样说,它们不是等价的,并且没有与 thread.Abort 等效的解决方案。 (3认同)
  • 我为答案添加了解释@ToolmakerSteve (3认同)

Gos*_*win 7

四年后,net7 中现在有了一个等价的东西!

代码执行的非合作中止: ControlledExecution

请小心,此方法可能会损坏进程,请参阅文档。

您可以在此处了解此类方法为何以及如何返回 .NET:https: //github.com/dotnet/runtime/issues/41291 https://github.com/dotnet/runtime/discussions/66480

  • 这实际上与“Thread.Abort”的建议相同,只是从“Thread”中移开,因此人们不会经常找到它。好吧,至少它就在那里,尽管他们花了足够长的时间。 (4认同)
  • *“`ControlledExecution.Run(Action, CancellationToken)`方法可能会破坏进程,不应在生产代码中使用。此方法运行可以异步中止的代码。虽然此方法是 .NET 7 的新方法,但它也被标记为已过时,以阻止您使用它。”* [链接](https://learn.microsoft.com/en-us/dotnet/fundamentals/syslib-diagnostics/syslib0046) (2认同)