我有一个async方法:
public async Task<string> GenerateCodeAsync()
{
string code = await GenerateCodeService.GenerateCodeAsync();
return code;
}
Run Code Online (Sandbox Code Playgroud)
我需要从同步方法中调用此方法.
如何在不必复制GenerateCodeAsync方法的情况下执行此操作以使其同步工作?
更新
然而找不到合理的解决方案
但是,我看到HttpClient已经实现了这种模式
using (HttpClient client = new HttpClient())
{
// async
HttpResponseMessage responseAsync = await client.GetAsync(url);
// sync
HttpResponseMessage responseSync = client.GetAsync(url).Result;
}
Run Code Online (Sandbox Code Playgroud) ConfigureAwait(false)在C#中使用await/async 时,有很多指南可供使用.
似乎一般的建议是ConfigureAwait(false)在库代码中使用,因为它很少依赖于同步上下文.
但是,假设我们正在编写一些非常通用的实用程序代码,它将函数作为输入.一个简单的例子可能是以下(不完整的)功能组合器,以简化基于任务的简单操作:
地图:
public static async Task<TResult> Map<T, TResult>(this Task<T> task, Func<T, TResult> mapping)
{
return mapping(await task);
}
Run Code Online (Sandbox Code Playgroud)
FlatMap:
public static async Task<TResult> FlatMap<T, TResult>(this Task<T> task, Func<T, Task<TResult>> mapping)
{
return await mapping(await task);
}
Run Code Online (Sandbox Code Playgroud)
问题是,我们应该ConfigureAwait(false)在这种情况下使用吗?我不确定上下文捕获是如何工作的.关闭.
一方面,如果组合器以功能方式使用,则不需要同步上下文.另一方面,人们可能会滥用API,并在提供的函数中执行依赖于上下文的内容.
一种选择是为每个场景(Map和/ MapWithContextCapture或某些东西)设置单独的方法,但感觉很难看.
另一种选择可能是将map/flatmap选项添加到a中ConfiguredTaskAwaitable<T>,但是由于等待不必实现接口,这会导致大量冗余代码,在我看来更糟糕.
是否有一种将责任转交给调用者的好方法,这样实现的库就不需要对提供的映射函数中是否需要上下文做出任何假设?
或者仅仅是一个事实,异步方法组成得不是很好,没有各种假设?
只是为了澄清一些事情:
ConfigureAwait(false)将导致空同步.上下文.正如一些答案所提到的那样,可以在方法中添加一个bool-flag,但正如我所看到的,这也不是太漂亮,因为它必须一直传播到API中(因为它有)更多"实用"功能,取决于上面显示的功能.
c# task synchronizationcontext task-parallel-library async-await
我想弄清楚是否应该在顶级请求上使用ConfigureAwait(false).从这个主题的某个权威读取这篇文章:http: //blog.stephencleary.com/2012/07/dont-block-on-async-code.html
......他推荐这样的东西:
public async Task<JsonResult> MyControllerAction(...)
{
try
{
var report = await _adapter.GetReportAsync();
return Json(report, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json("myerror", JsonRequestBehavior.AllowGet); // really slow without configure await
}
}
public async Task<TodaysActivityRawSummary> GetReportAsync()
{
var data = await GetData().ConfigureAwait(false);
return data
}
Run Code Online (Sandbox Code Playgroud)
...它表示在除顶级调用之外的每个await上使用ConfigureAwait(false). 但是,在执行此操作时,我的异常需要几秒钟才能返回调用者而不是使用它并让它立即返回.
调用异步方法的MVC控制器操作的最佳实践是什么?我应该在控制器本身中使用ConfigureAwait,还是只在使用等待请求数据等的服务调用中使用?如果我不在顶级调用中使用它,等待几秒钟的异常似乎有问题.我不需要HttpContext,并且我已经看过其他帖子,如果你不需要上下文,那么总是使用ConfigureAwait(false).
更新: 我在我的调用链中某处丢失了ConfigureAwait(false),导致异常无法立即返回.但是问题仍然存在,是否应该在顶层使用ConfigureAwait(false).
它建议使用一个ConfigureAwait(false)当你可以随时,特别是在图书馆,因为它可以帮助避免死锁和提高性能.
我编写了一个大量使用异步的库(访问数据库的Web服务).图书馆的用户遇到了僵局,经过多次痛苦的调试和修补后,我将其追踪到了单独使用await Task.Yield().我使用过的其他任何地方.ConfigureAwait(false),但是不支持Task.Yield().
对于需要等效的情况,推荐的解决方案是Task.Yield().ConfigureAwait(false)什么?
我已经读过有关如何SwitchTo移除某个方法的内容.我可以看出为什么那可能是危险的,但为什么没有相应的Task.Yield().ConfigureAwait(false)?
编辑:
为了提供我的问题的进一步背景,这里有一些代码.我正在实现一个开源库,用于访问支持异步的DynamoDB(作为AWS的服务的分布式数据库).IX-Async库IAsyncEnumerable<T>提供了许多操作返回.该库不提供从以"块"提供行的数据源生成异步枚举的好方法,即每个异步请求返回许多项.所以我有自己的通用类型.该库支持预读选项,允许用户指定在调用实际需要之前应该请求多少数据.MoveNext()
基本上,这是如何工作的,我通过调用GetMore()和传递这些块之间的状态来请求块.我将这些任务放入chunks队列并将它们出列并将它们转换为我放在单独队列中的实际结果.这个NextChunk()方法就是问题所在.根据值的不同,ReadAhead我会在完成最后一个(最多)之后保持获取下一个块,直到需要一个值但不可用(无)或仅获取超出当前使用值的下一个块(一些).因此,获取下一个块应该并行/不阻止获取下一个值.枚举器代码是:
private class ChunkedAsyncEnumerator<TState, TResult> : IAsyncEnumerator<TResult>
{
private readonly ChunkedAsyncEnumerable<TState, TResult> enumerable;
private readonly ConcurrentQueue<Task<TState>> chunks = new ConcurrentQueue<Task<TState>>();
private readonly Queue<TResult> results = new Queue<TResult>();
private CancellationTokenSource cts = new CancellationTokenSource();
private TState lastState;
private TResult current;
private …Run Code Online (Sandbox Code Playgroud) 我从比我更聪明的人那里多次阅读了这个建议,并且几乎没有注意事项:始终使用ConfigureAwait(false)内部库代码.所以我很确定我知道答案,但我想成为100%.场景是我有一个库,它包含了一些其他异步库.
图书馆代码:
public async Task DoThingAsyc() {
// do some setup
return await otherLib.DoThingAsync().ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)
申请代码:
// need to preserve my synchronization context
await myLib.DoThingAync();
// do I have my context here or did my lib lose it?
Run Code Online (Sandbox Code Playgroud) 这是一个WinForms代码:
async void Form1_Load(object sender, EventArgs e)
{
// on the UI thread
Debug.WriteLine(new { where = "before",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread });
var tcs = new TaskCompletionSource<bool>();
this.BeginInvoke(new MethodInvoker(() => tcs.SetResult(true)));
await tcs.Task.ContinueWith(t => {
// still on the UI thread
Debug.WriteLine(new { where = "ContinueWith",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread });
}, TaskContinuationOptions.ExecuteSynchronously).ConfigureAwait(false);
// on a pool thread
Debug.WriteLine(new { where = "after",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread });
}
Run Code Online (Sandbox Code Playgroud)
输出:
{ where = before, ManagedThreadId = 10, IsThreadPoolThread = False }
{ where = ContinueWith, … 基于众多书籍和博客,包括这里的优秀书籍,很明显当一个人写一个暴露助手异步方法的DLL库,即包装器方法时,通常认为内部完成实际异步方法的I/O任务的最佳实践在这样的线程池线程上(为了简洁起见,下面显示了伪代码,我将其HttpClient用作示例)
public Async Task<HttpResponseMessage> MyMethodAsync(..)
{
...
var httpClient = new HttpClient(..);
var response = await httpClient.PostAsJsonAsync(..).ConfigureAwait(false);
...
return response;
}
Run Code Online (Sandbox Code Playgroud)
这里的关键是使用ConfigureAwait(false)IO任务完成发生在线程池线程而不是原始线程上下文,从而可能防止死锁.
我的问题是从来电者的角度来看.我对调用者和上面的方法调用之间存在多层方法调用的情况特别感兴趣,如下例所示.
CallerA -> Method1Async -> Method2Async -> finally the above MyMethodAsync
Run Code Online (Sandbox Code Playgroud)
是仅仅ConfigureAwait(false)使用最终方法还是应该确保Method1Async并在Method2Async内部调用其异步方法ConfigureAwait(false)?将它包含在所有这些中间方法中似乎很愚蠢,特别是如果Method1Async并且Method2Async只是最终调用的重载MyMethodAsync.有任何想法,请指教!
更新了示例 所以如果我有一个带有以下私有异步方法的库,
private async Task<string> MyPrivateMethodAsync(MyClass myClass)
{
...
return await SomeObject.ReadAsStringAsync().ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)
我应该确保以下公共重载方法还包括ConfigureAwait(false),如下所示?
public async Task<string> MyMethodAsync(string from)
{
return await MyPrivateMethodAsync(new (MyClass() { From …Run Code Online (Sandbox Code Playgroud) .net c# task-parallel-library async-await io-completion-ports
在使用时await,默认情况下SynchronizationContext捕获(如果存在)并且await使用该上下文执行(继续块)之后的代码块(这导致线程上下文切换).
public async Task DoSomethingAsync()
{
// We are on a thread that has a SynchronizationContext here.
await DoSomethingElseAsync();
// We are back on the same thread as before here
//(well sometimes, depending on how the captured SynchronizationContext is implemented)
}
Run Code Online (Sandbox Code Playgroud)
虽然此默认值可能在您希望在异步操作完成后返回UI线程的UI上下文中有意义,但它似乎没有意义作为大多数其他方案的默认值.对于内部库代码来说当然没有意义,因为
在我看来,微软已经决定了错误的默认值.
现在我的问题:
有没有其他(最好是更好的)方法来解决这个问题,而不是混淆await我的代码中的所有调用.ConfigureAwait(false)?这很容易忘记,使代码可读性降低.
更新:await Task.Yield().ConfigureAwait(false);在每个方法的开头
调用是否足够?如果这可以保证我将在一个没有SynchronizationContextaferwards 的线程上,所有后续await调用都不会捕获任何上下文.
我最近写了以下代码:
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
{ …Run Code Online (Sandbox Code Playgroud) 我遇到了以下有关何时何地使用的文章ConfigureAwait(false),但无法得到答案。
您不需要 ConfigureAwait(false),但仍可在库和 UI 应用程序中使用它。(例如 Xamarin、WinForms 等)
https://blog.stephencleary.com/2017/03/aspnetcore-synchronization-context.html
此链接说相反的答案
为所有服务器端代码调用 ConfigureAwait 的最佳实践
我的问题:
场景 1:下面的代码作为后台服务运行。
我的问题:ConfigureAwait(false)无论何时await都需要像下面的 A 和 B 一样使用:
[Service(Name = "com.MainApplicationService", Label = "Main Application Service", Exported = false)]
public class MainApplicationService : Android.App.Service
{
public override IBinder OnBind(Intent intent)
{
return null;
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
await InitAsync().ConfigureAwait(false); //line A
Task.Run(async () => await …Run Code Online (Sandbox Code Playgroud) c# task-parallel-library xamarin.android async-await xamarin
async-await ×10
c# ×10
.net ×5
asynchronous ×3
asp.net ×1
asp.net-mvc ×1
azure ×1
c#-5.0 ×1
task ×1
xamarin ×1