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 方法。
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:
OperationCanceledException可以很容易地吞咽,而ThreadAbortException总是在月底再次上调catch块所以第三部分库可以通过即使包含常规catch块一个很好的机会被中止。更新:
如果您足够自信/绝望,您可以使用通过反射ThreadEx.Abort调用 的方法Thread.AbortInternal。尽管不能保证它会是 .NET Core 中的长期解决方案。
虽然我不完全同意Thread.Abort过时,因为它是关闭您没有影响的例程的好最后机会工具,但我也站在一边,必须不惜一切代价避免堕胎,因为它可以有讨厌的副作用。如果您是整个代码库的作者,则总是可以避免的。
更新 2:
似乎AbortInternal从那时起就被删除了。至少当前的 .NET Core 源不包含这样的方法。
您可以使用 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 来实现更干净。我在这里说的是,线程在被操作系统或其他块阻止之前不会被中断。因此,如果线程从不阻塞,则永远不会抛出异常,因此线程可能会完成而不会被中断。
四年后,net7 中现在有了一个等价的东西!
代码执行的非合作中止: ControlledExecution
请小心,此方法可能会损坏进程,请参阅文档。
您可以在此处了解此类方法为何以及如何返回 .NET:https: //github.com/dotnet/runtime/issues/41291 https://github.com/dotnet/runtime/discussions/66480
| 归档时间: |
|
| 查看次数: |
4551 次 |
| 最近记录: |