Parallel.Invoke与等待/异步任务的性能

时间:2019-04-03 12:46:17

标签: c# .net multithreading parallel-processing task

具有2种这样的核心类:

public void ReadAllData(List<Entity> entities){

        //processing
        foreach(Entity e in entities){
            ReadSingleData(entities[i]);
        }   
        //processing
}



  public void ReadSingleData(Entity entity){
        //processing
        db.ReadFromDataBase();
        //processing
    }

我需要提高从数据库读取,填充集合等的性能。 我首先将此实现与Task Parallel库一起使用:

Action[] actions = new Action[entities.Count);
public void ReadAllData(List<Entity> entities){

        //processing
        for(int i = 0; i< entities.Count;i++){
            actions[i] = new Action(() => ReadSingleData(entities[i]));
        }   
        //processing
}
ParallelOptions op = new ParallelOptions();
op.MaxDegreeOfParallelism = 6; //number of logical cores
Parallel.Invoke(op, actions);

我使用了异步/等待方法:

Task<bool>[] tasks = new Task<bool>[entities.Count];
public void ReadAllData(List<Entity> entities){

        //processing
        for(int i = 0; i< entities.Count;i++){
            tasks[i] = ReadSingleData(entities[i]);
        }   
        Task.WaitAll(tasks);
        //processing
}

public async Task<bool> ReadSingleData(Entity entity){
    //processing
    await Task.Run(() =>
        db.ReadFromDataBase();
    });
    //processing
    return flag;
}

以上只是伪代码。通过任务执行,我的速度提高了约35-40%(约30秒)。我的问题是并行库如何在内部工作?库是否使用第二种实现之类的任务?第一次实施会出什么问题? 谢谢。

2 个答案:

答案 0 :(得分:1)

由于第二种方法中的异步来自<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0"> <xsl:output method="text"/> <xsl:template match="x"> <xsl:for-each select="b/a"> <xsl:value-of select="w" /> </xsl:for-each> </xsl:template> </xsl:stylesheet> ,因此它们应该大致相同。我的猜测是Task.Run()比6大很多。您通常不需要设置MaxDegreeOfParallelism。

但是您应该考虑使用entities.Count并且没有Task.Run()来正确地进行异步操作。

答案 1 :(得分:0)

区别在于,在某些情况下,Parallel.Invoke不会为计划执行的每个动作生成任务,并且其行为类似于Parallel.ForEach。更详细地讲,如果任务数大于显式指定的并行度(如您的情况)或由内核数定义,则Parallel.Invoke拆分要分批调用的操作,以适应要求的并行度。单个批处理中的操作将顺序执行。相比之下,Task.Run在绝大多数情况下会在线程池中安排执行时间。

在CPU密集型计算的情况下,Parallel.Invoke逻辑将获胜,因为它意味着有效利用可用资源而不会引起争用。受I / O限制的操作会改变这种情况,因为在Parallel.Invoke情况下,并且同时Parallel.Invoke操作无法完全利用I / O带宽时,您可能会发现很多I / O争用数量是有限的(I / O发生时可能正在执行的其他操作可能只是批量等待)。 Task.Run在这种情况下具有优势,因为没有并行度上限,它可以覆盖其余的争用。这就是为什么在特定情况下它可能会提高性能的原因。但是,这有一个缺点:如果不受控制,Task.Run的应用可能会导致以下情况:线程池中的任务超载,并且必须生成新线程来服务传入的任务(当计划的I / O任务数量超过I / O带宽),进而可能导致性能下降。这就是为什么处理I / O限制操作的推荐方法是使用async/await,因为它依赖于确保最佳I / O利用率的内部机制(例如I / O完成端口)。