Task inlining and Task.Wait
本文关键字:Task Wait inlining and | 更新日期: 2023-09-27 18:13:45
我刚刚意识到,当我从任务内启动任务并调用Task.Wait
时,新任务将不会内联,而调用task。结果将始终内联任务。
当我们用RAII模式(在ExecuteWithCancel
中实现)包装任务时,内联将重用分配的资源,并且更可取。
但是我们有时想要等待一段时间,然后在此之后取消任务。等待代码如下所示:
using (var cts = new CancellationTokenSource())
{
// Task scheduler decides whether to execute synchronous or asynchronous
var task = new Task<TResult>(() => ExecuteWithCancel<TResult>(cts.Token, nameOfTaskPerformer, arguments), cts.Token)
if (timeout==TimeSpan.Zero || task.Wait(timeout)) // this creates an all or nothing timeout
return task.Result;
cts.Cancel();
throw new TimeoutException("");
}
当timeout为TimeSpan.Zero
时,任务是内联的,否则它总是使用另一个线程。
是否有一个简单的方法来重新设计这段代码使用内联和等待/超时?
很确定那是不可能的。假设您在线程A上运行以下代码:
var task = Task.Factory.StartNew(() => Thread.Sleep(Timeout.Infinite));
task.Wait(5000);
如果任务是内联的,线程A将无限期阻塞-超时后它将如何唤醒?查看参考源(Task.cs),我们可以确切地看到:
internal bool InternalWait(int millisecondsTimeout, CancellationToken cancellationToken)
{
...
// we will attempt inline execution only if an infinite wait was requested
// Inline execution doesn't make sense for finite timeouts and if a cancellation token was specified
// because we don't know how long the task delegate will take.
if (millisecondsTimeout == Timeout.Infinite && !cancellationToken.CanBeCanceled &&
WrappedTryRunInline() && IsCompleted)
{
returnValue = true;
}
else
{
returnValue = CompletedEvent.Wait(millisecondsTimeout, cancellationToken);
}
根据你的问题,为了从有限超时的内联中受益,你必须在任务本身内部实现超时逻辑,也许是这样的:
ExecuteWithCancel<TResult>(cts.Token, TimeSpan timeout, nameOfTaskPerformer, arguments)
然后使用常规的Wait()
(或Result
)。