Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用多个任务从大型集合中检索所有记录_C#_Multithreading_Concurrency_Task - Fatal编程技术网

C# 使用多个任务从大型集合中检索所有记录

C# 使用多个任务从大型集合中检索所有记录,c#,multithreading,concurrency,task,C#,Multithreading,Concurrency,Task,我正在开发一个调用外部服务的应用程序,它必须将外部集合的所有条目添加到本地集合中。目前的问题是,外部收集可能超过1000条记录,但返回的搜索结果最多只能包含20项 为了提高速度,我认为使用一组任务是前进的方向,因此我提出了以下代码: int totalCount = returnedCol.total_count; while (totalCount > myDict.Count) { int numberOfTasks = // l

我正在开发一个调用外部服务的应用程序,它必须将外部集合的所有条目添加到本地集合中。目前的问题是,外部收集可能超过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());
        }
int totalCount=returnedCol.total\u count;
while(totalCount>myDict.Count)
{
int numberOfTasks=//计算要运行多少任务的逻辑
List taskList=新列表();
对于(int i=1;i
{
返回ExternalCall.GetData(pageNumber,pageSize);
});
睡眠(100);
taskList.Add(testTask);
testTask.ContinueWith(o=>
{
foreach(testTask.Result.dataiwant中的ExternalDataRecord dataiwant)
{
如果(!myDict.ContainsKey(dataiwant.id))
myDict.GetOrAdd(dataiwant.id,dataiwant);
}
});
}
Task.WaitAll(taskList.ToArray());
}
然而,这并不能产生所有的结果。
pageNumber
变量每次都正确递增,但似乎并不是所有任务结果都在分析中(因为较小数据集上单个线程上的相同逻辑返回所有预期结果)。此外,我还尝试在一个链(而不是一个循环)中声明单个任务,测试数据全部返回。似乎传入
Thread.Sleep()
的值越高,结果添加到本地集合的次数就越多(但这并不理想,因为这意味着该过程需要更长的时间!)


目前,在600条记录的样本中,我只得到了大约150-200条添加到
myDict
集合中。我遗漏了一些明显的东西吗?

您遗漏了一个事实,即
ContinueWith()
会导致另一个任务,并且您没有添加
任务列表

更好的方法是使用.NET4.5以来提供的
async
/
wait
。它为解决方案提供了一种不那么繁重的方法

您可以将算法更改为如下所示:

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);
       }
 }
公共异步任务进程()
{
int totalCount=返回的COL.total\U计数;
while(totalCount>myDict.Count)
{
int numberOfTasks=//计算要运行多少任务的逻辑
List taskList=新列表();
对于(int i=1;i
GetData(pageNumber,pageSize)).ConfigureWait(false);
foreach(结果中的ExternalDataRecord dataiwant.dataiwant)
{
myDict.GetOrAdd(dataiwant.id,dataiwant);
}
}

async
关键字告诉编译器以后将有一个
await
await
基本上通过
调用处理
continuew的细节。如果您确实希望
外部调用
在另一个任务中发生,那么您只需
等待该调用的结果

我认为,如果您对代码采取功能性更强、命令性更少的方法,那么您就不太可能遇到难以理解的问题。我认为类似的事情也会产生与您所期望的效果相同的效果:

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

“计算要运行多少任务的逻辑”似乎与此相关。另外,您对
(页码、页面大小)的肯定程度如何
是否正确?我可以看出您面临的问题来自于在实际到达收集结束之前停止循环,或者请求大量重叠的数据块。
pageSize
在我的代码中从不更改,因为它总是设置为最高可用值(即20).我已经测试了
pageNumber
值是否每次正确递增,并且它开始运行(每次递增20个),但开始变得不稳定。增加Thread.Sleep period会对此产生影响,但由于我以线程安全的方式增加Thread.Sleep period,我不明白为什么会发生这种情况。此外,为了进行测试,我将外部数据源设置为60个项的集合,并尝试通过60个任务一次检索一个项,但仍然没有获得所有的数据我的集合中的ata(除非我增加睡眠时间),如果(!myDict.ContainsKey(dataiwant.id))
,则无需执行
,它只会减慢进程。只需始终调用
myDict.GetOrAdd(dataiwant.id,dataiwant);
,在新记录上,它添加新记录,而在现有记录上,它实际上什么也不做,因为您不保存它返回的值
返回另一个尚未完成的任务。如果您不等待该任务完成,则无法保证您的收集已完成。此外,您的
页码是否是方法调用的本地项?困扰我的一件事是隐含需要
任务。延迟
。如果代码正确,则确实不需要这样做。@BerinLoritsch:我同意。我保留了它,因为我知道一些服务会拒绝请求,如果它们被同一个源太快地命中,所以这可能是为了限制,但我添加了另一个没有它的示例。感谢回答,Berin设法首先回答,所以我从它们的代码开始,但我是否应该遇到进一步的问题s我肯定也会看一看:)@ChrisWright:那很好。我会小心你使用的外部
循环,但是:如果一个项目从s中移除