在 C# 中的异步方法中,我需要推迟执行(将执行返回给调用者),并在继续执行之前清除同步上下文。
方法开头的语句
await Task.Delay(1).ConfigureAwait(false);
可以做到这一点,但由于延迟,平均性能损失为 15 毫秒。
推迟和清除执行上下文的更好方法是检查当前执行上下文并调用
Task.Yield
(它保留执行上下文,如果有的话)或 Task.Delay(1).ConfigureAwait(false)
:
public async Task Test()
{
if (SynchronizationContext.Current is null)
{
await Task.Yield();
}
else
{
await Task.Delay(1).ConfigureAwait(false);
}
// Deferred code
}
你知道是否有更简单的方法来推迟当前任务的执行(就像
Task.Yield
那样)并同时清除同步上下文?
我不能这样做,因为 Task.Yield 和 Task.ConfigureAwait 对可等待/等待值使用不同的结构:
public async ??? TaskYieldNoSyncContext() =>
(SynchronizationContext.Current is null)
? Task.Yield()
: Task.Delay(1).ConfigureAwait(false);
public async Task Test()
{
await TaskYieldNoSyncContext();
// Deferred code
}
因此,我创建以下内容将ConfiguredTaskAwaitable/ConfiguredTaskAwaiter 和 YieldAwaitable/YieldAwaiter 合并到单个嵌套结构中:
具有“await”关键字所需实现的接口:
public interface ICustomAwaitable
{
public ICustomAwaiter GetAwaiter();
public interface ICustomAwaiter : ICriticalNotifyCompletion
{
public bool IsCompleted { get; }
public void GetResult();
}
}
ConfiguredTaskAwaitable/ConfiguredTaskAwaiter 的实现:
public struct CustomAwaitableTask : ICustomAwaitable
{
private readonly ConfiguredTaskAwaitable _awaitable;
public CustomAwaitableTask(ConfiguredTaskAwaitable awaitable)
{
_awaitable = awaitable;
}
public ICustomAwaitable.ICustomAwaiter GetAwaiter() { return new CustomAwaiterTask(_awaitable.GetAwaiter()); }
public struct CustomAwaiterTask : ICustomAwaitable.ICustomAwaiter
{
private readonly ConfiguredTaskAwaitable.ConfiguredTaskAwaiter _awaiter;
public CustomAwaiterTask(ConfiguredTaskAwaitable.ConfiguredTaskAwaiter awaiter)
{
_awaiter = awaiter;
}
public bool IsCompleted => _awaiter.IsCompleted;
public void OnCompleted(Action continuation) => _awaiter.OnCompleted(continuation: continuation);
public void UnsafeOnCompleted(Action continuation) => _awaiter.UnsafeOnCompleted(continuation: continuation);
public void GetResult() => _awaiter.GetResult();
}
}
YieldAwaitable/YieldAwaiter 的实现:
public struct CustomAwaitableYield : ICustomAwaitable
{
private readonly YieldAwaitable _awaitable;
public CustomAwaitableYield(YieldAwaitable awaitable)
{
_awaitable = awaitable;
}
public ICustomAwaitable.ICustomAwaiter GetAwaiter() { return new CustomAwaiterYield(_awaitable.GetAwaiter()); }
public struct CustomAwaiterYield : ICustomAwaitable.ICustomAwaiter
{
private readonly YieldAwaitable.YieldAwaiter _awaiter;
public CustomAwaiterYield(YieldAwaitable.YieldAwaiter awaiter)
{
_awaiter = awaiter;
}
public bool IsCompleted => _awaiter.IsCompleted;
public void OnCompleted(Action continuation) => _awaiter.OnCompleted(continuation: continuation);
public void UnsafeOnCompleted(Action continuation) => _awaiter.UnsafeOnCompleted(continuation: continuation);
public void GetResult() => _awaiter.GetResult();
}
}
以及根据当前同步上下文返回这些实现之一的静态方法:
public static ICustomAwaitable YieldNoSyncContext()
{
if (SynchronizationContext.Current is null)
{
return new CustomAwaitableYield(Task.Yield());
}
else
{
return new CustomAwaitableTask(Task.Delay(1).ConfigureAwait(false));
}
}
这样我就可以推迟任务并以最小的性能影响清除同步:
public async Task Test()
{
await TaskYieldNoSyncContext();
// Deferred code
}
ConfigureAwaitOptions.ForceYielding
选项。
在已经完成的
上强制执行await
,就像Task
尚未完成一样,这样当前的异步方法将被迫放弃其执行。Task
使用示例:
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
await
之后的延续将在ThreadPool
线程上运行,无论SynchronizationContext.Current
如何。
顺便说明一下,
await Task.Delay(1);
并不保证延续是异步的。理论上,操作系统有可能在创建之后、等待 Task
之前立即挂起当前线程。操作系统可以随时挂起任何线程,持续时间通常在 10 到 30 毫秒之间(demo)。当线程恢复时,Task
可能已经完成,在这种情况下,await
之后的延续将在同一线程上运行。