gpt4 book ai didi

c# - 异步/等待与手工制作的延续 : is ExecuteSynchronously cleverly used?

转载 作者:太空狗 更新时间:2023-10-29 21:12:27 25 4
gpt4 key购买 nike

我最近写了下面的代码:

    Task<T> ExecAsync<T>( string connectionString, SqlCommand cmd, Func<SqlCommand, T> resultBuilder, CancellationToken cancellationToken = default(CancellationToken) )
{
var tcs = new TaskCompletionSource<T>();

SqlConnectionProvider p;
try
{
p = GetProvider( connectionString );
Task<IDisposable> openTask = p.AcquireConnectionAsync( cmd, cancellationToken );
openTask
.ContinueWith( open =>
{
if( open.IsFaulted ) tcs.SetException( open.Exception.InnerExceptions );
else if( open.IsCanceled ) tcs.SetCanceled();
else
{
var execTask = cmd.ExecuteNonQueryAsync( cancellationToken );
execTask.ContinueWith( exec =>
{
if( exec.IsFaulted ) tcs.SetException( exec.Exception.InnerExceptions );
else if( exec.IsCanceled ) tcs.SetCanceled();
else
{
try
{
tcs.SetResult( resultBuilder( cmd ) );
}
catch( Exception exc ) { tcs.TrySetException( exc ); }
}
}, TaskContinuationOptions.ExecuteSynchronously );
}
} )
.ContinueWith( _ =>
{
if( !openTask.IsFaulted ) openTask.Result.Dispose();
}, TaskContinuationOptions.ExecuteSynchronously );
}
catch( Exception ex )
{
tcs.SetException( ex );
}
return tcs.Task;
}

这按预期工作。使用 async/await 编写的相同代码(显然)更简单:

async Task<T> ExecAsync<T>( string connectionString, SqlCommand cmd, Func<SqlCommand, T> resultBuilder, CancellationToken cancellationToken = default(CancellationToken) )
{
SqlConnectionProvider p = GetProvider( connectionString );
using( IDisposable openTask = await p.AcquireConnectionAsync( cmd, cancellationToken ) )
{
await cmd.ExecuteNonQueryAsync( cancellationToken );
return resultBuilder( cmd );
}
}

我快速查看了 2 个版本生成的 IL:async/await 更大(不足为奇)但我想知道 async/await 代码生成器是否分析了一个延续实际上是同步使用的事实TaskContinuationOptions.ExecuteSynchronously 它可以...我在 IL 生成的代码中找不到它。

如果有人知道或对此有任何线索,我很乐意知道!

最佳答案

I was wondering if the async/await code generator analyses the fact that a continuation is actually synchronous to use TaskContinuationOptions.ExecuteSynchronously where it can... and I failed to find this in the IL generated code.

await continuations - without ConfigureAwait(continueOnCapturedContext: false) - 异步或同步执行取决于同步上下文的存在当它到达 await 点时正在执行您的代码的线程。如果 SynchronizationContext.Current != null,则进一步的行为取决于 SynchronizationContext.Post 的实现。

例如,如果您在 WPF/WinForms 应用程序的主 UI 线程上,您的延续将在同一线程上执行,但仍然异步,在消息循环的 future 迭代中。它将通过 SynchronizationContext.Post 发布。前提是先行任务已在线程池线程或不同的同步上下文(例如 Why a unique synchronization context for each Dispatcher.BeginInvoke callback?)上完成。

如果前面的任务已在具有相同同步上下文的线程(例如 WinForm UI 线程)上完成,await 延续将同步(内联)执行。 SynchronizationContext.Post 将不会在这种情况下使用。

在没有同步上下文的情况下,await 延续将在先前任务已完成的同一线程上同步执行。

这就是它与使用 TaskContinuationOptions.ExecuteSynchronously 实现的 ContinueWith 的不同之处,它根本不关心初始线程或完成线程的同步上下文,并始终同步执行延续(尽管如此,仍有 exceptions to this behavior)。

您可以使用 ConfigureAwait(continueOnCapturedContext: false) 来更接近所需的行为,但它的语义仍然不同于 TaskContinuationOptions.ExecuteSynchronously。事实上,它指示调度程序在具有任何同步上下文的线程上运行延续,因此您可能会遇到 ConfigureAwait(false) pushes the continuation to thread pool 的情况,而您可能一直期待同步执行。

还相关:Revisiting Task.ConfigureAwait(continueOnCapturedContext: false).

关于c# - 异步/等待与手工制作的延续 : is ExecuteSynchronously cleverly used?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30919601/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com