sco*_*732 1 .net c# asynchronous async-await
我正在尝试编写一些包含非基于任务的库(MSDeploy API)的库代码.基本上,我从远程服务器获取东西并进行分析.我正在使用TaskCompletionSource包装同步MSDeploy API b/c虽然它不是基于任务的,但我可以获得CancellationToken支持来使用该库的CancelCallback委托."获取东西"代码作为同步API公开,但它受I/O限制.分析代码是I/O和CPU密集型的,并且已经基于任务.它不会产生任何任务/线程(我将在调用代码中执行此操作).
我最近一直在阅读很多关于这些东西的博客,但我仍然试图了解async/await/ConfigureAwait()并找出编写和调用复合方法(GetAndAnalyzeStuff)的最佳方法.几个问题:
下面的代码(使用.NET 4.5.1):
public Task<Stuff> GetStuff(CancellationToken token)
{
var tcs = new TaskCompletionSource<Stuff>(tcs);
try
{
var stuff = new Stuff();
using (var stuffGetter = new StuffGetter())
{
stuffGetter.CancelCallback = () => cancellationToken.IsCancellationRequested;
for (var x = 0; x < 10; x++)
{
if (token.IsCancellationRequested)
{
tcs.SetCancelled();
return tcs.Task;
}
// StuffGetter's doing IO & would ideally have an API w/Task<Stuff>, but
// it doesn't and it's not my code.
var thing = stuffGetter.GetSomething();
stuff.AddThing(thing);
}
tcs.SetResult(stuff);
}
}
catch (SomeNonTplFriendlyExceptionThatMeansSomethingWasCancelled sntfetmswc)
{
tcs.SetCancelled();
}
catch (Exception exc)
{
tcs.SetException(exc);
}
return tcs.Task;
}
public async Task<StuffAnalysis> AnalyzeStuff(Stuff stuff, CancellationToken token)
{
var allTasks = new List<Task>();
using (var stuffAnalyzer = new StuffAnalyzer())
{
foreach (var thing in stuff)
{
allTasks.Add(stuffAnalyzer.AnalyzeThingAsync(thing));
}
await Task.WhenAll(allTasks);
}
return stuffAnalyzer.Result;
}
public async Task<StuffAnalysis> GetAndAnalyzeStuff(CancellationToken token)
{
var stuff = await GetStuff(token); // or should this be GetStuff.Wait() or maybe GetStuff.Result?
var analysis = await AnalyzeStuff(stuff, token);
return analysis;
}
public static void Main()
{
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
try
{
// Since GetStuff is synchronous and I don't want to block, use Task.Run() -- right?
var task = Task.Run(() => GetAndAnalyzeStuff(cts.Token), cts.Token);
// or var task = Task.Run(async () => await GetAndAnalyzeStuff(cts.Token), cts.Token); ?
Console.WriteLine("Getting and anlyzing stuff in the background.");
await task;
}
catch (OperationCancelledException)
{
Console.WriteLine("There was a problem.");
}
catch (Exception)
{
Console.WriteLine("There was a problem.");
}
}
Run Code Online (Sandbox Code Playgroud)
由于您没有自然异步API,我不建议使用TaskCompletionSource<T>.您可以使用CancellationToken同步API 的完全支持,如下所示:
public Stuff GetStuff(CancellationToken token)
{
var stuff = new Stuff();
using (var stuffGetter = new StuffGetter())
{
stuffGetter.CancelCallback = () => token.IsCancellationRequested;
for (var x = 0; x < 10; x++)
{
token.ThrowIfCancellationRequested();
var thing = stuffGetter.GetSomething();
stuff.AddThing(thing);
}
return stuff;
}
}
Run Code Online (Sandbox Code Playgroud)
编写任务返回方法时,遵循TAP指南非常重要.在这种情况下,命名约定意味着您AnalyzeStuff应该被调用AnalyzeStuffAsync.
你能告诉我在某处是否需要ConfigureAwait(false)吗?如果是这样,你怎么知道?
您应该使用,ConfigureAwait(false)除非您需要方法中的上下文("上下文"通常是客户端应用程序的UI上下文,或ASP.NET应用程序的请求上下文).您可以在我的MSDN文章中async找到有关最佳实践的更多信息.
所以,假设StuffAnalyzer.Result没有任何类型的UI线程依赖或类似的东西,我会这样写AnalyzeStuffAsync:
public async Task<StuffAnalysis> AnalyzeStuffAsync(Stuff stuff, CancellationToken token)
{
var allTasks = new List<Task>();
using (var stuffAnalyzer = new StuffAnalyzer())
{
foreach (var thing in stuff)
{
allTasks.Add(stuffAnalyzer.AnalyzeThingAsync(thing));
}
await Task.WhenAll(allTasks).ConfigureAwait(false);
}
return stuffAnalyzer.Result;
}
Run Code Online (Sandbox Code Playgroud)
您的GetAndAnalyzeStuffAsync情况更复杂,在方法中同时包含阻塞代码和异步代码.在这种情况下,最好的方法是将其作为异步API公开,但要注意它是阻塞的明确注释.
// <summary>Note: this method is not fully synchronous; it will block the calling thread.</summary>
public async Task<StuffAnalysis> GetAndAnalyzeStuffAsync(CancellationToken token)
{
var stuff = GetStuff(token);
var analysis = await AnalyzeStuff(stuff, token).ConfigureAwait(false);
return analysis;
}
Run Code Online (Sandbox Code Playgroud)
这可以简化为:
// <summary>Note: this method is not fully synchronous; it will block the calling thread.</summary>
public Task<StuffAnalysis> GetAndAnalyzeStuffAsync(CancellationToken token)
{
var stuff = GetStuff(token);
return AnalyzeStuff(stuff, token);
}
Run Code Online (Sandbox Code Playgroud)
如何在调用代码(下面的Main())中处理Task.Run()调用?
您正确使用它.我在博客上描述了这种情况.在控制台应用程序中,这样使用并不常见Task.Run,但这样做是没有错的.Task.Run通常用于释放UI应用程序中的UI线程.
我假设Task.Run比Task.Factory.StartNew b/c更合适我正在做异步的东西.
是的.
| 归档时间: |
|
| 查看次数: |
1980 次 |
| 最近记录: |