背景
我有一个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
正在为正在运行的线程注入(inject)异常,这导致它立即停止。现在,我将代码移到了dotnet核心-如您所知,当您调用
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
? 最佳答案
在不中止的情况下,唯一的解决方案是经常轮询取消请求,因此在您提到的所有while (!canceled)
解决方案之后。
这只是部分正确。例如,可以这样重写以响应:
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
块的末尾重新引发,因此即使包含常规的catch块,第3部分库也有可能被中止。 更新:
如果您有足够的信心/绝望,则可以使用
ThreadEx.Abort
方法,该方法通过反射来调用Thread.AbortInternal
。尽管不能保证它将是.NET Core中的长期解决方案。尽管我并不完全同意弃用
Thread.Abort
,因为它是关闭您没有影响的例程的好机会,否则我还是不惜一切代价避免使用堕胎可以有nasty side effects。如果您是整个代码库的作者,那么总是可以避免的。更新2:
从那时起,似乎
AbortInternal
已被删除。至少current .NET Core source不包含这种方法。