C# C并行-向正在迭代的集合中添加项,或等效项?

C# C并行-向正在迭代的集合中添加项,或等效项?,c#,parallel-processing,parallel.foreach,C#,Parallel Processing,Parallel.foreach,现在,我有一个C程序,它重复执行以下步骤: 从数据库中获取当前任务列表 使用Parallel.ForEach,完成每个任务 然而,其中一些任务是长期运行的。这会延迟其他挂起任务的处理,因为我们只在程序开始时查找新任务 现在,我知道修改正在迭代的集合是不可能的,对吗?但是在C并行框架中是否有一些等效的功能允许我在处理列表中的项目的同时向列表中添加工作?下面是一个可以尝试的方法示例。我认为您希望摆脱Parallel.ForEaching,转而使用异步编程,因为您需要在结果完成时检索结果,而不是在离散

现在,我有一个C程序,它重复执行以下步骤:

从数据库中获取当前任务列表 使用Parallel.ForEach,完成每个任务 然而,其中一些任务是长期运行的。这会延迟其他挂起任务的处理,因为我们只在程序开始时查找新任务


现在,我知道修改正在迭代的集合是不可能的,对吗?但是在C并行框架中是否有一些等效的功能允许我在处理列表中的项目的同时向列表中添加工作?

下面是一个可以尝试的方法示例。我认为您希望摆脱Parallel.ForEaching,转而使用异步编程,因为您需要在结果完成时检索结果,而不是在离散的块中检索,这些块可能包含长时间运行的任务和快速完成的任务

这种方法使用简单的顺序循环从异步任务列表中检索结果。在这种情况下,您应该可以安全地使用一个简单的非线程安全可变列表,因为列表的所有变异都是在同一线程中顺序发生的

注意,这种方法使用任务。当一个循环对于大任务列表来说不是非常有效的时候,在这种情况下你应该考虑另一种方法。请参阅此博客:

此示例基于:


一般来说,不允许在迭代集合时修改集合,这是正确的。但您可以使用其他方法:

使用。代码可能类似于:

var actionBlock = new ActionBlock<MyTask>(
    task => DoWorkForTask(task),
    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded });

while (true)
{
    var tasks = GrabCurrentListOfTasks();
    foreach (var task in tasks)
    {
        actionBlock.Post(task);

        await Task.Delay(someShortDelay);
        // or use Thread.Sleep() if you don't want to use async
    }
}

您是否使用计时器从数据库中获取任务?如果是,请向任务表中添加状态列,并将任务标记为正在处理。任务完成后,将其标记为已完成。因此,在计时器中,您应该只抓取状态为空的任务。关于任务列表,它应该在ProcessTasks方法中声明。这意味着每次计时器运行时,你都会得到一个新的列表。请原谅,但我不太明白这是如何解决问题的。我们确实在计时器上抓取任务,并具有处理和完成列。问题是,一旦我们抓取了任务列表并开始使用Parallel.ForEach处理它们,我们就会被困在该ForEach中,直到所有处理完成。我想我们可以把并行处理抛到一个任务中,这样主线程就可以继续它的计时器了。这意味着如果你有5个任务,它们应该被独立处理,并且你应该在这5个线程启动后立即退出循环,对吗?你看过线程安全吗?。这可能会对您有所帮助。您确信ForEach在启动每个线程后都会返回吗?我可以发誓,它会一直阻塞,直到所有线程都完成为止。@KosalaW Parallel.ForEach将不会完成,直到它枚举了所有元素或被中断为止。它很像传统的foreach循环,但可以并行执行。非常感谢你的回答,但我认为这并不是我面临的核心问题。我不想过早地处理已经完成的结果,事实上,不需要进行后期处理。相反,我希望并行处理任务列表,并且在处理运行时,将更多任务添加到列表中以并行处理。等待任务。在第一个任务完成之前,任何调用都将被阻止。如果该任务是长时间运行的,那么在执行GetOutstandingTasks调用之前将需要很长时间,这正是我现在遇到的问题。@Eric task。如果在第一个任务完成之前不阻止任何任务,它将阻止直到列表中的任何任务完成。这意味着您可以完成和处理大量快速运行的任务,同时等待长时间运行任务的结果。长时间运行的任务在完成之前不会出现。@Eric我更新了我的答案,希望它现在的措辞更清楚。那么,只有在任务完成时才查询新任务?当问题明确指出某些任务可能需要很长时间时,这听起来不是一个好方法。例如,如果当前只有一个这样的任务正在处理中,那么您的代码在获得新任务之前会等待很长时间。@svick这正是我的问题。一些长时间运行的任务阻碍了我们处理一些可能短时间运行的任务。
var actionBlock = new ActionBlock<MyTask>(
    task => DoWorkForTask(task),
    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded });

while (true)
{
    var tasks = GrabCurrentListOfTasks();
    foreach (var task in tasks)
    {
        actionBlock.Post(task);

        await Task.Delay(someShortDelay);
        // or use Thread.Sleep() if you don't want to use async
    }
}
var collection = new BlockingCollection<MyTask>();

Task.Run(async () =>
{
    while (true)
    {
        var tasks = GrabCurrentListOfTasks();
        foreach (var task in tasks)
        {
            collection.Add(task);

            await Task.Delay(someShortDelay);
        }
    }
});

Parallel.ForEach(collection.GetConsumingPartitioner(), task => DoWorkForTask(task));