看到有人做完作业真是令人耳目一新。
首先,从 .NET 4 开始(今天仍然如此),TAP 是 .NET 中异步工作流的首选技术。任务很容易组合,如果它们提供了真正的功能,那么并行化 Web 服务调用就会变得轻而易举。Task<T>
-返回API。现在你已经“伪造”它了Task.Run
,目前这可能足以满足您的目的。当然,您的线程池线程将花费大量时间进行阻塞,但如果服务器负载不是很高,即使这不是理想的做法,您也可以很好地摆脱它。
您只需要修复代码中潜在的竞争条件(最后会详细介绍)。
如果您想遵循最佳实践,那么您可以选择真正的 TAP。如果您的 API 提供Task
- 开箱即用的返回方法,这很简单。如果没有,游戏还没有结束,因为 APM 和 EAP 可以轻松转换为 TAP。 MSDN 参考:https://msdn.microsoft.com/en-us/library/hh873178(v=vs.110).aspx https://msdn.microsoft.com/en-us/library/hh873178(v=vs.110).aspx
我还将在此处提供一些转换示例。
APM(摘自另一个SO问题):
MessageQueue
不提供ReceiveAsync
方法,但我们可以通过它让它打球Task.Factory.FromAsync
:
public static Task<Message> ReceiveAsync(this MessageQueue messageQueue)
{
return Task.Factory.FromAsync(messageQueue.BeginReceive(), messageQueue.EndPeek);
}
...
Message message = await messageQueue.ReceiveAsync().ConfigureAwait(false);
如果您的网络服务代理有BeginXXX
/EndXXX
方法,这就是要走的路。
EAP
假设您有一个旧的 Web 服务代理,源自SoapHttpClientProtocol
,仅使用基于事件的异步方法。您可以将它们转换为 TAP,如下所示:
public Task<long> GetPointAsyncTask(this PointWebService webService, int nationalCode)
{
TaskCompletionSource<long> tcs = new TaskCompletionSource<long>();
webService.GetPointAsyncCompleted += (s, e) =>
{
if (e.Cancelled)
{
tcs.SetCanceled();
}
else if (e.Error != null)
{
tcs.SetException(e.Error);
}
else
{
tcs.SetResult(e.Result);
}
};
webService.GetPointAsync(nationalCode);
return tcs.Task;
}
...
using (PointWebService service = new PointWebService())
{
long point = await service.GetPointAsyncTask(123).ConfigureAwait(false);
}
汇总结果时避免竞争
关于聚合并行结果,您的 TAP 循环代码是almost是的,但是你需要避免改变你的共享状态Task
机构,因为它们可能会并行执行。共享状态Result
就你而言 - 这是某种集合。如果这个集合不是线程安全的(即如果它是一个简单的List<long>
),那么你有一个竞争条件,你可能会得到异常和/或丢弃结果Add
(我假设AddRange
您的代码中有一个拼写错误,但如果不是 - 上面的内容仍然适用).
一个简单的异步友好重写可以修复你的竞赛,如下所示:
List<Task<long>> tasks = new List<Task<long>>();
foreach (BaseClass item in Clients) {
tasks.Add(item.GetPointAsync(MasterLogId, mobileNumber));
}
long[] results = await Task.WhenAll(tasks).ConfigureAwait(false);
如果你决定偷懒并坚持Task.Run
目前的解决方案,更正后的版本将如下所示:
List<Task<long>> tasks = new List<Task<long>>();
foreach (BaseClass item in Clients)
{
Task<long> dodgyThreadPoolTask = Task.Run(
() => item.GetPoint(MasterLogId, mobileNumber)
);
tasks.Add(dodgyThreadPoolTask);
}
long[] results = await Task.WhenAll(tasks).ConfigureAwait(false);