编译器会给你关于你的警告async
拉姆达。仔细阅读;它告诉你它不是异步的。使用没有意义async
那里。另外,不要使用async void
.
由于您的底层 API 是阻塞的 - 并且无法更改它 - 异步代码不是一个选项。我建议使用几个Task.Run
calls or Parallel.For
,但不能两者兼而有之。所以让我们使用并行。实际上,让我们使用并行 LINQ,因为您转变一个序列。
没有意义做RetrieveProductAvailablity
异步;它只做除节流之外的阻塞工作,并且并行方法具有更自然的节流支持。这使得你的方法看起来像:
private ProductSearchResult RetrieveProductAvailablity(int productId, CancellationToken cancellationToken)
{
... // no mutex code
LegacyApp app = new LegacyApp();
bool success = app.RetrieveProductAvailability(productId);
... // no mutex code
}
然后您可以进行并行处理,如下所示:
public void Start()
{
ProductSearchResult[] results = products.AsParallel().AsOrdered()
.WithCancellation(cts.Token).WithDegreeOfParallelism(2)
.Select(product => RetrieveProductAvailability(product.ID, cts.Token))
.ToArray();
// Logic for waiting on indiviaul tasks and reporting results
}
从你的 UI 线程中,你可以call该方法使用Task.Run
:
async void MyUiEventHandler(...)
{
await Task.Run(() => processor.Start());
}
这使您的业务逻辑保持干净(仅同步/并行代码),并负责将这项工作移出 UI 线程(使用Task.Run
)属于UI层。
Update:我添加了一个电话AsOrdered
确保结果数组的顺序与产品序列相同。这可能是必要的,也可能不是必要的,但由于原始代码保留了顺序,因此该代码现在也这样做了。
Update:由于您需要在每次检索后更新 UI,因此您可能应该使用Task.Run
对于每一个而不是AsParallel
:
public async Task Start()
{
var tasks = products.Select(product =>
ProcessAvailabilityAsync(product.ID, cts.Token));
await Task.WhenAll(tasks);
}
private SemaphoreSlim mutex = new SempahoreSlim(2);
private async Task ProcessAvailabilityAsync(int id, CancellationToken token)
{
await mutex.WaitAsync();
try
{
var result = await RetrieveProductAvailability(id, token);
// Logic for reporting results
}
finally
{
mutex.Release();
}
}