相当于Thread.Abort的dotnet核心

时间:2018-11-25 07:37:26

标签: c# multithreading .net-core

背景

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

此代码从标准.NET Framework迁移,我创建了一个Thread对象,该对象运行Start(model)方法。然后代码就像:

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

Thread.Abort正在为正在运行的线程注入异常,这导致它立即停止。

现在,我将代码移到了dotnet core -您可能知道,当您致电Thread.Abort()时,会收到以下消息:

System.PlatformNotSupportedException: Thread abort is not supported on this platform.
   at System.Threading.Thread.Abort()
   at ...

目标

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

Thread.sleep(61000); // 61 seconds. should be stop after 60 seconds.

进度

在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];
    }
}

这个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();
}

预期:“结束”不会打印到屏幕上。实际:“结束”打印。是否有其他方法可以杀死Task

2 个答案:

答案 0 :(得分:6)

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

答案 1 :(得分:2)

在不放弃的情况下,唯一的解决方案是经常轮询取消请求,因此在您提到的所有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);
}

当然,并非每个任务都可以很容易地重新编写以轮询取消操作。如果您处于深层次的通话链中,那么检查每个级别的取消可能会很痛苦。因此,您还可以使用CancellationToken.ThrowIfCancellationRequested方法,如果有取消请求,该方法将抛出OperationCanceledException。通常,我通常不会只为自己抛出异常并将其用于控制​​流,但是取消是可以证明其合理性的领域之一。

Abort相比,此解决方案当然有一些局限性:

  • 您将无法取消不支持取消且无法重构的第三方程序
  • OperationCanceledException可以很容易地被吞下,而ThreadAbortException总是在catch块的末尾重新升起,因此即使遇到以下情况也有可能中止第三部分库包含常规捕获块。

更新

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

尽管我并不完全同意放弃Thread.Abort,因为它是关闭您没有其他影响的例程的好机会,但我还是必须堕胎不惜一切代价避免,因为它可以拥有nasty side effects。如果您是整个代码库的作者,那么总是可以避免的。