C# 是否有方法将.ConfigureAwait(false)应用于整个任务子树?

C# 是否有方法将.ConfigureAwait(false)应用于整个任务子树?,c#,.net,asynchronous,async-await,C#,.net,Asynchronous,Async Await,我有一个类,它并行运行数千个异步任务,远远超过工作线程的数量。如果我不在wait调用上执行.ConfigureAwait(false),那么我的性能会低很多,但是将.ConfigureAwait(false)附加到每个wait调用会很单调,并降低代码的可读性。我正在寻找一种方法,在生成这些任务的函数中指定某种空上下文,以便它们内部的每个等待调用自动不关心SynchronizationContext。可能吗 更新:我在谷歌上搜索了一下,它看起来就像我已经进入了一个带有的函数。ConfigureAw

我有一个类,它并行运行数千个异步任务,远远超过工作线程的数量。如果我不在wait调用上执行
.ConfigureAwait(false)
,那么我的性能会低很多,但是将
.ConfigureAwait(false)
附加到每个wait调用会很单调,并降低代码的可读性。我正在寻找一种方法,在生成这些任务的函数中指定某种空上下文,以便它们内部的每个等待调用自动不关心SynchronizationContext。可能吗

更新:我在谷歌上搜索了一下,它看起来就像我已经进入了一个带有
的函数。ConfigureAwait(false)
当前的SynchronizationContext将为null,我不需要在子函数调用中关心它。对吗

看起来,一旦我已经在.configurewait(false)的函数中,当前SynchronizationContext将为null,我不需要在子函数调用中关心它。对吗

那么实际情况是:

  • 您在一个异步函数中
  • 你开始一项任务
  • 在任务完成之前,对该任务执行
    等待
    (配置为
    false
所以,这不是一个值得依赖的好情况。具体来说,如果任务很快完成(在您配置的
wait
被点击之前),那么您仍将处于原始上下文中。请注意,即使你不期望它发生,这种情况也可能发生;例如,移动设备在缓存web请求方面非常积极

要“跳出”同步上下文,只需将其包装在一个
任务中。运行
-这将非常简短地使用线程池线程,并在该线程池上下文中执行所有子代代码

看起来,一旦我已经在.configurewait(false)的函数中,当前SynchronizationContext将为null,我不需要在子函数调用中关心它。对吗

那么实际情况是:

  • 您在一个异步函数中
  • 你开始一项任务
  • 在任务完成之前,对该任务执行
    等待
    (配置为
    false
所以,这不是一个值得依赖的好情况。具体来说,如果任务很快完成(在您配置的
wait
被点击之前),那么您仍将处于原始上下文中。请注意,即使你不期望它发生,这种情况也可能发生;例如,移动设备在缓存web请求方面非常积极


要“跳出”同步上下文,只需将其包装在一个
任务中。运行
-这将非常简短地使用线程池线程,并执行该线程池上下文中的所有子代代码。

是的,您可以。通过重置当前SynchronizationContext

在顶级异步方法中,执行以下操作:

async Task TopLevelAsync()
{
    var syncContext = SynchronizationContext.Current;
    SynchronizationContext.SetSynchronizationContext(null);
    try
    {
        // no need for ConfigureAwait(false)
        await SubTask1Async();
        await SubTask2Async();
    }
    finally
    {
         SynchronizationContext.SetSynchronizationContext(syncContext);
    }
}
一旦我已经在.ConfigureAwait(false)的函数中,当前SynchronizationContext将为null,我不需要在子函数调用中关心它。对吗

不幸的是,事实并非如此。这是一个危险的假设。原因如下:

// in a top-level async method..
await FooAsync().ConfigureAwait(false);

async Task FooAsync()
{
    var result = await BarAsync().ConfigureAwait(false);
    // we are inside the ConfigureAwait(false), or in the
    // continuation after ConfigureAwait(false), so at this
    // point the SynchronizationContext must be null, right?
    // No it's not.
}

Task<bool> BarAsync()
{
    return Task.FromResult(true);
}
//在顶级异步方法中。。
wait fooancy().configurewait(false);
异步任务fooancy()
{
var result=await BarAsync().ConfigureAwait(false);
//我们在ConfigureWait(false)中,或者在
//configurewait之后的continuation(false),因此
//同步上下文必须为空,对吗?
//不,不是。
}
任务同步()
{
返回Task.FromResult(true);
}

从上面的例子来看,
BarAsync
是同步完成的,这意味着
fooancy
方法后面的状态机会立即继续执行,而无需重新调度。如果没有重新调度逻辑,
ConfigureAwait(false)
不会被考虑在内,因此SynchronizationContext永远不会被重置。

是的,您可以。通过重置当前SynchronizationContext

在顶级异步方法中,执行以下操作:

async Task TopLevelAsync()
{
    var syncContext = SynchronizationContext.Current;
    SynchronizationContext.SetSynchronizationContext(null);
    try
    {
        // no need for ConfigureAwait(false)
        await SubTask1Async();
        await SubTask2Async();
    }
    finally
    {
         SynchronizationContext.SetSynchronizationContext(syncContext);
    }
}
一旦我已经在.ConfigureAwait(false)的函数中,当前SynchronizationContext将为null,我不需要在子函数调用中关心它。对吗

不幸的是,事实并非如此。这是一个危险的假设。原因如下:

// in a top-level async method..
await FooAsync().ConfigureAwait(false);

async Task FooAsync()
{
    var result = await BarAsync().ConfigureAwait(false);
    // we are inside the ConfigureAwait(false), or in the
    // continuation after ConfigureAwait(false), so at this
    // point the SynchronizationContext must be null, right?
    // No it's not.
}

Task<bool> BarAsync()
{
    return Task.FromResult(true);
}
//在顶级异步方法中。。
wait fooancy().configurewait(false);
异步任务fooancy()
{
var result=await BarAsync().ConfigureAwait(false);
//我们在ConfigureWait(false)中,或者在
//configurewait之后的continuation(false),因此
//同步上下文必须为空,对吗?
//不,不是。
}
任务同步()
{
返回Task.FromResult(true);
}

从上面的例子来看,
BarAsync
是同步完成的,这意味着
fooancy
方法后面的状态机会立即继续执行,而无需重新调度。如果没有重新调度逻辑,
ConfigureAwait(false)
不会被考虑在内,因此同步上下文永远不会被重置。

我认为这是不可能的,我认为在
ConfigureAwait
中使用
true
作为默认值也是一个很大的错误。@dasblinkenlight,不同意默认值“error”。当您异步调用该方法时,“默认情况下”您希望继续将在相同的“上下文”中执行。@Fabio据我所知,它真正重要的地方是UI。在其他地方,您不关心它,至少您不太关心性能降低和死锁的可能性。@dasblinkenlight,通常异步方法的调用层次结构将在UI中结束,在UI中所有结果都将被“等待”并处理异常。所以,默认情况下在同一上下文中执行结果似乎是合乎逻辑的解决方案。当然,这个解决方案可以用“大的”和“小的端点”一样的方式来论证