C# 中在没有同步上下文的情况下推迟任务执行的最简单方法

问题描述 投票:0回答:2

在 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
那样)并同时清除同步上下文?

c# async-await yield deferred-execution
2个回答
0
投票

我不能这样做,因为 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
}

0
投票

从 .NET 8 开始,您可以使用

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
之后的延续将在同一线程上运行。

© www.soinside.com 2019 - 2024. All rights reserved.