我正在开发一个调用外部服务的应用程序,并且必须将外部集合的所有条目添加到本地集合中。目前的问题是外部集合可能超过1000条记录,但返回的搜索结果最多只能包含20个项目。
为了速度,我认为使用一系列任务将是前进的方向,所以我提出了以下代码:
int totalCount = returnedCol.total_count;
while (totalCount > myDict.Count)
{
int numberOfTasks = // logic to calculate how many tasks to run
List<Task> taskList = new List<Task>();
for (int i = 1; i <= numberOfTasks; i++)
{
Interlocked.Add(ref pageNumber, pageSize);
Task<SearchResponse> testTask = Task.Run(() =>
{
return ExternalCall.GetData(pageNumber, pageSize);
});
Thread.Sleep(100);
taskList.Add(testTask);
testTask.ContinueWith(o =>
{
foreach (ExternalDataRecord dataiwant in testTask.Result.dataiwant)
{
if (!myDict.ContainsKey(dataiwant.id))
myDict.GetOrAdd(dataiwant.id, dataiwant);
}
});
}
Task.WaitAll(taskList.ToArray());
}
但是,这不会产生所有结果。 pageNumber
变量每次都正确递增,但似乎并非所有任务结果都被分析(因为较小数据集上的单个线程上的相同逻辑会返回所有预期结果)。此外,我尝试在链(而不是循环)中声明单个任务,并且全部返回测试数据。似乎我传入Thread.Sleep()
的值越高,结果被添加到本地集合中的越多(但这并不理想,因为这意味着该过程需要更长时间!)
目前,在600条记录的样本中,我只在myDict
集合中添加了约150-200条记录。我错过了一些明显的东西吗?
答案 0 :(得分:2)
我认为如果你对代码采用更实用,更少命令的方法,那么你遇到难以理解的问题的可能性就会大大降低。我认为这样的事情会产生同样的效果:
int totalCount = returnedCol.total_count;
var tasks = Enumerable.Range(1, totalCount / pageSize)
.Select(async page => {
await Task.Delay(page * 100);
return ExternalCall.GetData(page, pageSize));
})
.ToArray();
myDict = (await Task.WhenAll(tasks))
.ToDictionary(dataiwant => dataiwant.id);
上面的代码假设您仍然希望在限制请求之间等待100毫秒。如果您只是在那里Thread.Sleep()
尝试解决您遇到的问题,可以进一步简化它:
int totalCount = returnedCol.total_count;
var tasks = Enumerable.Range(1, totalCount / pageSize)
.Select(async page => await Task.Run(() => ExternalCall.GetData(page, pageSize)))
.ToArray();
myDict = (await Task.WhenAll(tasks))
.ToDictionary(dataiwant => dataiwant.id);
答案 1 :(得分:0)
您错过了ContinueWith()
导致其他任务的事实,并且您没有添加您的taskList
。
更好的方法是使用自.NET 4.5以来可用的async
/ await
。它为解决方案提供了一种不那么重的方法。
您可以将算法更改为:
public async Task Process()
{
int totalCount = returnedCol.total_count;
while (totalCount > myDict.Count)
{
int numberOfTasks = // logic to calculate how many tasks to run
List<Task> taskList = new List<Task>();
for (int i = 1; i <= numberOfTasks; i++)
{
Interlocked.Add(ref pageNumber, pageSize);
taskList.Add(ProcessPage(pageNumber, pageSize));
}
await Task.WhenAll(taskList.ToArray());
}
}
private async Task ProcessPage(int pageNumber, int pageSize)
{
SearchResponse result = await Task.Run(() =>
ExternalCall.GetData(pageNumber, pageSize)).ConfigureAwait(false);
foreach (ExternalDataRecord dataiwant in result.dataiwant)
{
myDict.GetOrAdd(dataiwant.id, dataiwant);
}
}
async
关键字告诉编译器稍后会有await
。 await
基本上处理了ContinueWith
电话的详细信息。如果您真的希望ExternalCall
在其他任务中发生,那么您只需await
来自该调用的结果。