C# &引用;等待任务。配置等待(false)";对;等待ContextSwitcher.SwitchToThreadPool();
广泛建议如下使用C# &引用;等待任务。配置等待(false)";对;等待ContextSwitcher.SwitchToThreadPool();,c#,.net,.net-core,async-await,task-parallel-library,C#,.net,.net Core,Async Await,Task Parallel Library,广泛建议如下使用ConfigureAwait(false): await Do1Async().ConfigureAwait(false); // ... await Do2Async().ConfigureAwait(false); // ... await Do3Async().ConfigureAwait(false); // ... try { await Do1Async(); // UI thread await ContextSwitcher.SwitchToThread
ConfigureAwait(false)
:
await Do1Async().ConfigureAwait(false);
// ...
await Do2Async().ConfigureAwait(false);
// ...
await Do3Async().ConfigureAwait(false);
// ...
try
{
await Do1Async(); // UI thread
await ContextSwitcher.SwitchToThreadPool();
await Do2Async(); // Thread pool thread
}
catch (Exception)
{
... // Unknown thread
}
IIRC,同时,人们普遍不鼓励使用这样的东西,它会将异步执行流上下文切换到池线程,从而有助于避免ConfigureAwait
在我的方法中横行:
await ContextSwitcher.SwitchToThreadPool(); // this was even removed from async CTP
await Do1Async();
// ...
await Do2Async();
// ...
await Do3Async();
// ...
为什么第一个选项被认为是一个好的实践,而这个选项不是,特别是考虑到等待Do1Async()之后的代码。ConfigureAwait(false)
将在与等待ContextSwitcher.SwitchToThreadPool()之后的代码完全相同的条件下继续运行
此外,还有另一种选择:
await Task.Run(async () => {
await Do1Async();
// ...
await Do2Async();
// ...
await Do3Async();
// ...
});
IIRC,这仍然比ContextSwitcher
选项好,但是为什么呢?
最后,还有一个有趣的方法:
以下是同步ContextRemover的相关部分:
像这样删除同步上下文安全吗?在等待新的SynchronizationContextRemover()之后,AFAIU会影响整个同步作用域。
这个
SynchronizationContextRemover
比ContextSwitcher
好多少,而且它可能少了一个到池线程的开关?正如其他人所指出的,配置等待(false)
对于现代代码来说不太必要(特别是,因为ASP.NET内核已经成为主流)。在这一点上,是否在你的图书馆中使用它是一种判断;就个人而言,我仍然使用它,但我的主要异步库是非常低级的
特别是考虑到await Do1Async().ConfigureAwait(false)
之后的代码将以与await ContextSwitcher.SwitchToThreadPool()之后的代码完全相同的条件继续运行
条件并不完全相同-如果同步完成Do1Async
,则情况有所不同
为什么第一个选项被认为是一个好的实践,而这个选项不是
同样,“切换器”方法允许这样的代码:
await Do1Async().ConfigureAwait(false);
// ...
await Do2Async().ConfigureAwait(false);
// ...
await Do3Async().ConfigureAwait(false);
// ...
try
{
await Do1Async(); // UI thread
await ContextSwitcher.SwitchToThreadPool();
await Do2Async(); // Thread pool thread
}
catch (Exception)
{
... // Unknown thread
}
具体地说,catch
和finally
块可以在未知的线程上下文中运行,具体取决于运行时行为。可以在多个线程上运行的代码更难维护。这就是它被从异步CTP中删除的主要原因(也要记住,对于当时的语言,您不能在catch
或finally
中等待,因此无法切换到所需的上下文)
IIRC,这仍然比ContextSwitcher
选项好,但是为什么呢?
我认为需要注意的是,这些都是不同的语义——特别是,它们都在说完全不同的东西:
await x.ConfigureAwait(false)
表示“我不在乎恢复哪个线程。它可以是同一个线程或线程池线程,无论什么。”注意:它没有说“切换到线程池线程。”
wait ContextSwitcher()
说“切换到此上下文并继续执行”
等待任务。Run(…)
说“在线程池线程上运行此代码,然后恢复我。”
在所有这些中,我更喜欢第一个和第三个。我使用ConfigureAwait(false)
表示“此方法不关心它恢复的线程”,我使用Task.Run
表示“在后台线程上运行此代码”。我不喜欢“切换器”方法,因为我发现它会降低代码的可维护性
像这样删除同步上下文安全吗?在等待新的SynchronizationContextRemover()之后,AFAIU会影响整个同步作用域。
对,;棘手的部分是它需要影响同步作用域。这就是为什么没有Func
或Func
的原因,它们的作用几乎相同。NoContext
和SynchronizationContextRemover
之间的一个主要区别是,地雷强制使用一个范围(lambda),其中没有上下文,而remover是一个“切换器”的形式。同样,我的方法强制代码说“在没有上下文的情况下运行此代码”,而切换器说“在我的方法中的这一点上,删除上下文,然后继续执行”。在我看来,显式作用域的代码更易于维护(再次考虑catch
/finally
块),这就是我使用这种API风格的原因
这个SynchronizationContextRemover
比ContextSwitcher
好多少,而且它可能少了一个到池线程的切换
SynchronizationContextRemover
和NoContext
都位于同一线程上;它们只是暂时删除该线程上的上下文ContextSwitcher实际上会切换到线程池线程。正如其他人所指出的,ConfigureAwait(false)
对于现代代码来说不太必要(特别是因为ASP.NET核心已经成为主流)。在这一点上,是否在你的图书馆中使用它是一种判断;就个人而言,我仍然使用它,但我的主要异步库是非常低级的
特别是考虑到await Do1Async().ConfigureAwait(false)
之后的代码将以与await ContextSwitcher.SwitchToThreadPool()之后的代码完全相同的条件继续运行
条件并不完全相同-如果同步完成Do1Async
,则情况有所不同
为什么第一个选项被认为是一个好的实践,而这个选项不是
同样,“切换器”方法允许这样的代码:
await Do1Async().ConfigureAwait(false);
// ...
await Do2Async().ConfigureAwait(false);
// ...
await Do3Async().ConfigureAwait(false);
// ...
try
{
await Do1Async(); // UI thread
await ContextSwitcher.SwitchToThreadPool();
await Do2Async(); // Thread pool thread
}
catch (Exception)
{
... // Unknown thread
}
具体地说,catch
和finally
块可以在未知的线程上下文中运行,具体取决于运行时行为。可以使用r的代码