.net 如何动态限制Parallel.Foreach创建的任务的并发执行
我正在创建一个对一组资源执行命令的应用程序。例如,资源可以是服务器。例如,任务可以是“ping”或“对数据库索引进行碎片整理”(这些都是示例,因为我无法揭示应用程序的真实性质)。 应用程序使用TPL(Parallel.ForEach)并发执行这些命令 现在我已经到了必须限制对存储在公共资源上的资源并发执行命令的地步。作为一个例子,这里有一个必须进行碎片整理的DB索引列表.net 如何动态限制Parallel.Foreach创建的任务的并发执行,.net,task-parallel-library,parallel.foreach,taskscheduler,.net,Task Parallel Library,Parallel.foreach,Taskscheduler,我正在创建一个对一组资源执行命令的应用程序。例如,资源可以是服务器。例如,任务可以是“ping”或“对数据库索引进行碎片整理”(这些都是示例,因为我无法揭示应用程序的真实性质)。 应用程序使用TPL(Parallel.ForEach)并发执行这些命令 现在我已经到了必须限制对存储在公共资源上的资源并发执行命令的地步。作为一个例子,这里有一个必须进行碎片整理的DB索引列表 Task# | Server | DB | Index Name -----------------------------
Task# | Server | DB | Index Name
---------------------------------
T1 | 1 | DB1 | A
T2 | 1 | DB1 | B
T3 | 1 | DB1 | C
T4 | 1 | DB2 | D
T5 | 2 | DB3 | E
T6 | 2 | DB3 | F
T7 | 3 | DB4 | G
T8 | 4 | DB5 | H
T9 | 6 | DB6 | I
为了防止服务器上出现大规模并行磁盘I/O,我必须将索引碎片整理任务的执行限制为每台服务器一个。
这意味着任务T7、T8和T9可以同时运行。只有一个任务T1-T4必须同时运行。任务T5和T6也是如此。
例如,还可以将每个资源的并发执行限制为2个任务。我怎样才能做到这一点
我已经看过TaskScheduler类,但是TaskScheduler似乎不能拒绝任务(QueueTask方法)。另外,一个定制的分区员也不会把我带到那里
我能想到的唯一解决方案是创建我自己的Parallel.ForEach实现,它可以完全解决这个限制
有谁有更好的主意吗
如上所述,这是一个示例用例。因此,请不要发布这样的答案:为此创建SQL代理作业。该示例不是最合适的。同时尝试对多个索引进行碎片整理将导致性能下降,因为碎片整理操作将争夺相同的CPU和磁盘资源。并行碎片整理操作运行得更快的唯一方法是将每个索引存储在不同的磁盘(阵列)中 您还可以在一个脚本中编写所有碎片整理语句并运行它 在一般情况下,如果您希望将某些作业/操作排队,以便以有限的并行性并行执行,则相应的库是和。ActionBlock允许您将消息发布到块以进行并发执行。默认情况下,一次只处理一条消息。您只需在创建块时传递不同的DOP参数即可处理更多消息
var dopOptions=new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = maxDop
}
var defragBlock=new ActionBlock<string)(indexName=>MyDefragMethod(indexName), dopOptions);
//Post all the indexes. Only maxDop will be processed at a time
foreach(var indexName in indexesList)
{
defragBlock.Post(indexName);
}
//Notify the block that we are done
defragBlock.Complete();
//and wait for all remaining indexes to finish
await defragBlock.Completion;
您还可以将块存储在由服务器名称键入的字典中,以便在循环中选择正确的块。那么这不是一个好的示例。Parallel.ForEach用于数据并行,而不是一次运行多个命令。只是不要用它来做这个。此外,为什么要并行运行多个命令?它们将在同一个数据库、IO、CPU上运行,因此您不会获得比使用所有命令运行一个批处理更好的性能。最有可能的情况是,由于所有命令都在争夺相同的资源,因此性能会变得更差。不过,一般来说,ActionBlock
var block1=new ActionBlock<string)(indexName=>MyDefragMethod(indexName,connStr1), dopOptions);
var block2 =new ActionBlock<string)(indexName=>MyDefragMethod(indexName,connStr2), dopOptions);
foreach(var indexName in indexesList1)
{
block1.Post(indexName);
}
foreach(var indexName in indexesList2)
{
block2.Post(indexName);
}
//Notify the block that we are done
block1.Complete();
block2.Complete();
//and wait for all remaining indexes to finish
await Task.WhenAll(block1.Completion,block2.Completion);