"await task.ConfigureAwait(false)" 对比 "await ContextSwitcher.SwitchToThreadPool()"
"await task.ConfigureAwait(false)" versus "await ContextSwitcher.SwitchToThreadPool()"
广泛推荐这样使用 ConfigureAwait(false)
:
await Do1Async().ConfigureAwait(false);
// ...
await Do2Async().ConfigureAwait(false);
// ...
await Do3Async().ConfigureAwait(false);
// ...
IIRC,同时人们普遍不鼓励使用这样的东西 ContextSwitcher
,这会将异步执行流上下文切换到池线程,从而可能有助于避免 ConfigureAwait
我的方法:
await ContextSwitcher.SwitchToThreadPool(); // this was even removed from async CTP
await Do1Async();
// ...
await Do2Async();
// ...
await Do3Async();
// ...
为什么第一个选项被认为是一个好的做法而这个不是,特别是考虑到 await Do1Async().ConfigureAwait(false)
之后的代码将在完全相同的条件下继续作为 await ContextSwitcher.SwitchToThreadPool()
?
之后的代码
还有一个选择:
await Task.Run(async () => {
await Do1Async();
// ...
await Do2Async();
// ...
await Do3Async();
// ...
});
IIRC,这仍然比 ContextSwitcher
选项好,但为什么呢?
最后还有这个有趣的做法:An alternative to ConfigureAwait(false) everywhere.
这是 the author's repo 中 SynchronizationContextRemover
的相关部分:
public void OnCompleted(Action continuation)
{
var prevContext = SynchronizationContext.Current;
try
{
SynchronizationContext.SetSynchronizationContext(null);
continuation();
}
finally
{
SynchronizationContext.SetSynchronizationContext(prevContext);
}
}
像那样删除同步上下文是否安全,AFAIU 会在 await new SynchronizationContextRemover()
之后影响整个同步范围?
await new SynchronizationContextRemover();
// we are still on the same thread
// but the synchronization context has been removed,
// be careful...
// ...
await Do1Async();
// ...until now
这个 SynchronizationContextRemover
比 ContextSwitcher
有何优势,此外也许它少了一个到池线程的切换?
正如其他人指出的那样,ConfigureAwait(false)
对于现代代码来说不是那么必要(特别是,因为 ASP.NET Core 已经成为主流)。此时是否在你的库中使用它是一个判断调用;就个人而言,我仍然使用它,但我的主要异步库非常低级。
especially given the fact the code after await Do1Async().ConfigureAwait(false)
will continue on exactly the same conditions as the code after await ContextSwitcher.SwitchToThreadPool()
?
条件不完全相同 - 如果 Do1Async
同步完成,则存在差异。
Why is the 1st option considered a good practice and this one isn't
与 explained by Stephen Toub 一样,"switcher" 方法允许这样的代码:
try
{
await Do1Async(); // UI thread
await ContextSwitcher.SwitchToThreadPool();
await Do2Async(); // Thread pool thread
}
catch (Exception)
{
... // Unknown thread
}
具体来说,catch
和 finally
块可以 运行 在未知线程上下文中,具体取决于 运行 时间行为。可以在多个线程上 运行 的代码更难维护。这是它从 Async CTP 中删除的主要原因(还要记住,使用当时的语言,你不能在 catch
或 finally
中 await
,所以你无法切换到所需的上下文)。
IIRC, this is still better than the ContextSwitcher
option, but why?
我认为重要的是要注意这些都是不同的语义 - 特别是,它们都在说完全不同的事情:
await x.ConfigureAwait(false)
说 "I don't care what thread I resume on. It can be the same thread or a thread pool thread, whatever." 注意: 它 而不是 说 "switch to a thread pool thread."
await ContextSwitcher()
说 "Switch to this context and continue executing"。
await Task.Run(...)
说 "Run this code on a thread pool thread and then resume me."
在所有这些中,我更喜欢第一个和第三个。我用ConfigureAwait(false)
表示"this method doesn't care about the thread it resumes on",我用Task.Run
表示"run this code on a background thread"。我不喜欢 "switcher" 方法,因为我发现它使代码更难维护。
Is it safe to just remove the synchronization context like that, which AFAIU would affect the whole synchronous scope after await new SynchronizationContextRemover()
?
是;棘手的部分是它需要影响 synchronous 范围。这就是为什么没有 Func<Task>
或 Func<Task<T>>
overloads for my SynchronizationContextSwitcher.NoContext
method,它们的作用几乎相同。 NoContext
和 SynchronizationContextRemover
之间的一个主要区别是我的强制作用域(一个 lambda),其中没有上下文,并且移除器采用 "switcher" 的形式。因此,我再次强制代码说 "run this code without a context",而切换器说 "at this point in my method, remove the context, and then continue executing"。在我看来,明确范围的代码更易于维护(同样,考虑 catch
/finally
块),这就是我使用 API.
风格的原因
How is this SynchronizationContextRemover
better than ContextSwitcher
, besides perhaps it has one less switch to a pool thread?
SynchronizationContextRemover
和 NoContext
都在同一个线程上;他们只是暂时删除该线程上的上下文。 ContextSwitcher
确实切换到线程池线程。
广泛推荐这样使用 ConfigureAwait(false)
:
await Do1Async().ConfigureAwait(false);
// ...
await Do2Async().ConfigureAwait(false);
// ...
await Do3Async().ConfigureAwait(false);
// ...
IIRC,同时人们普遍不鼓励使用这样的东西 ContextSwitcher
,这会将异步执行流上下文切换到池线程,从而可能有助于避免 ConfigureAwait
我的方法:
await ContextSwitcher.SwitchToThreadPool(); // this was even removed from async CTP
await Do1Async();
// ...
await Do2Async();
// ...
await Do3Async();
// ...
为什么第一个选项被认为是一个好的做法而这个不是,特别是考虑到 await Do1Async().ConfigureAwait(false)
之后的代码将在完全相同的条件下继续作为 await ContextSwitcher.SwitchToThreadPool()
?
还有一个选择:
await Task.Run(async () => {
await Do1Async();
// ...
await Do2Async();
// ...
await Do3Async();
// ...
});
IIRC,这仍然比 ContextSwitcher
选项好,但为什么呢?
最后还有这个有趣的做法:An alternative to ConfigureAwait(false) everywhere.
这是 the author's repo 中 SynchronizationContextRemover
的相关部分:
public void OnCompleted(Action continuation)
{
var prevContext = SynchronizationContext.Current;
try
{
SynchronizationContext.SetSynchronizationContext(null);
continuation();
}
finally
{
SynchronizationContext.SetSynchronizationContext(prevContext);
}
}
像那样删除同步上下文是否安全,AFAIU 会在 await new SynchronizationContextRemover()
之后影响整个同步范围?
await new SynchronizationContextRemover();
// we are still on the same thread
// but the synchronization context has been removed,
// be careful...
// ...
await Do1Async();
// ...until now
这个 SynchronizationContextRemover
比 ContextSwitcher
有何优势,此外也许它少了一个到池线程的切换?
正如其他人指出的那样,ConfigureAwait(false)
对于现代代码来说不是那么必要(特别是,因为 ASP.NET Core 已经成为主流)。此时是否在你的库中使用它是一个判断调用;就个人而言,我仍然使用它,但我的主要异步库非常低级。
especially given the fact the code after
await Do1Async().ConfigureAwait(false)
will continue on exactly the same conditions as the code afterawait ContextSwitcher.SwitchToThreadPool()
?
条件不完全相同 - 如果 Do1Async
同步完成,则存在差异。
Why is the 1st option considered a good practice and this one isn't
与 explained by Stephen Toub 一样,"switcher" 方法允许这样的代码:
try
{
await Do1Async(); // UI thread
await ContextSwitcher.SwitchToThreadPool();
await Do2Async(); // Thread pool thread
}
catch (Exception)
{
... // Unknown thread
}
具体来说,catch
和 finally
块可以 运行 在未知线程上下文中,具体取决于 运行 时间行为。可以在多个线程上 运行 的代码更难维护。这是它从 Async CTP 中删除的主要原因(还要记住,使用当时的语言,你不能在 catch
或 finally
中 await
,所以你无法切换到所需的上下文)。
IIRC, this is still better than the
ContextSwitcher
option, but why?
我认为重要的是要注意这些都是不同的语义 - 特别是,它们都在说完全不同的事情:
await x.ConfigureAwait(false)
说 "I don't care what thread I resume on. It can be the same thread or a thread pool thread, whatever." 注意: 它 而不是 说 "switch to a thread pool thread."await ContextSwitcher()
说 "Switch to this context and continue executing"。await Task.Run(...)
说 "Run this code on a thread pool thread and then resume me."
在所有这些中,我更喜欢第一个和第三个。我用ConfigureAwait(false)
表示"this method doesn't care about the thread it resumes on",我用Task.Run
表示"run this code on a background thread"。我不喜欢 "switcher" 方法,因为我发现它使代码更难维护。
Is it safe to just remove the synchronization context like that, which AFAIU would affect the whole synchronous scope after
await new SynchronizationContextRemover()
?
是;棘手的部分是它需要影响 synchronous 范围。这就是为什么没有 Func<Task>
或 Func<Task<T>>
overloads for my SynchronizationContextSwitcher.NoContext
method,它们的作用几乎相同。 NoContext
和 SynchronizationContextRemover
之间的一个主要区别是我的强制作用域(一个 lambda),其中没有上下文,并且移除器采用 "switcher" 的形式。因此,我再次强制代码说 "run this code without a context",而切换器说 "at this point in my method, remove the context, and then continue executing"。在我看来,明确范围的代码更易于维护(同样,考虑 catch
/finally
块),这就是我使用 API.
How is this
SynchronizationContextRemover
better thanContextSwitcher
, besides perhaps it has one less switch to a pool thread?
SynchronizationContextRemover
和 NoContext
都在同一个线程上;他们只是暂时删除该线程上的上下文。 ContextSwitcher
确实切换到线程池线程。