gpt4 book ai didi

c# - 我应该等待使用 TaskCompletionSource 的同步 Task 吗?

转载 作者:太空宇宙 更新时间:2023-11-03 19:10:03 25 4
gpt4 key购买 nike

我正在尝试编写一些包装非基于任务的库 (MSDeploy API) 的库代码。本质上,我是从远程服务器获取内容并对其进行分析。我正在使用 TaskCompletionSource 来包装同步 MSDeploy API b/c,尽管它不是基于任务的,但我可以获得 CancellationToken 支持以使用该库的 CancelCallback 委托(delegate)。 “获取东西”代码作为同步 API 公开,但它是 I/O 绑定(bind)的。分析代码是 I/O 和 CPU 密集型的,并且已经是基于任务的。它不会产生任何任务/线程(我将在调用代码中这样做)。

我最近阅读了很多关于这方面的博客,但我仍在努力了解 async/await/ConfigureAwait() 并找出编写和调用复合方法的最佳方式 ( GetAndAnalyzeStuff)。几个问题:

  1. 是在 GetAndAnalyzeStuff 方法中同时等待基于 TaskCompletionSource 的调用和 TPL 异步调用,还是仅等待异步调用(我应该在调用 GetStuff 时使用 .Wait())。
  2. 你能告诉我是否需要 ConfigureAwait(false) 吗?如果是这样,你怎么知道?
  3. 如何处理调用代码(下面的 Main())中的 Task.Run() 调用?我更喜欢它在后台运行,我认为在库/API 代码中启动任务是不好的做法。我假设 Task.Run 比 Task.Factory.StartNew 更合适 b/c 我正在做异步的事情。

下面的代码(使用 .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.");
}
}

最佳答案

由于您没有天然异步 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;
}
}

当您编写任务返回方法时,遵循 TAP guidelines 很重要.在这种情况下,命名约定意味着您的 AnalyzeStuff应该叫AnalyzeStuffAsync .

Can you tell whether I need ConfigureAwait(false) somewhere? If so, how can you tell?

你应该使用 ConfigureAwait(false)除非您的方法中需要上下文(“上下文”通常是客户端应用程序的 UI 上下文,或 ASP.NET 应用程序的请求上下文)。您可以在我的 MSDN article on async best practices 中找到更多信息.

因此,假设 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;
}

你的 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;
}

可以简化为:

// <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);
}

How do I deal with the Task.Run() call in the calling code (Main() below)?

您正在正确使用它。我描述了this situation在我的博客上。在控制台应用程序中,使用 Task.Run 并不常见像这样,但这样做没有任何错误Task.Run通常用于释放 UI 应用程序中的 UI 线程。

I assume Task.Run is more appropriate than Task.Factory.StartNew b/c I'm doing async stuff.

是的,是的。

关于c# - 我应该等待使用 TaskCompletionSource 的同步 Task<T> 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21470350/

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