C# 优化并行选择导致并行执行

C# 优化并行选择导致并行执行,c#,parallel-processing,plinq,C#,Parallel Processing,Plinq,我试图找到执行以下流程的最佳方式 从流中获取X标识符的列表 对于每个X标识符,转到DB并使用复杂查询获得Y标识符的列表(应并行)) 合并列表并将其区分开来 对于每个Y标识符,执行一个长时间运行的任务(应该并行) 2-4中的所有流都应该在与1不同的线程中完成,这样一旦操作1调用操作2,它就会继续执行其他操作 当前我的流程如下所示(这是针对2-4的,在1之后调用): private void PerformActionAsync(列表XIds) { var affectedYIds=XIds.AsP

我试图找到执行以下流程的最佳方式

  • 从流中获取X标识符的列表
  • 对于每个X标识符,转到DB并使用复杂查询获得Y标识符的列表(应并行)
  • 合并列表并将其区分开来
  • 对于每个Y标识符,执行一个长时间运行的任务(应该并行)
  • 2-4中的所有流都应该在与1不同的线程中完成,这样一旦操作1调用操作2,它就会继续执行其他操作

    当前我的流程如下所示(这是针对2-4的,在1之后调用):

    private void PerformActionAsync(列表XIds)
    {
    var affectedYIds=XIds.AsParallel().SelectMany(xId=>
    {
    返回getaffectedyidslongtrunning(xId);
    }).Distinct();
    受影响的yId.ForAll(yId=>
    {
    执行长时间运行行动(yId);
    });
    }
    
    这不起作用,因为SelectMany和ForAll仍会阻止调用线程,我可以用新任务创建替换ForAll,但SelectMany仍会阻止调用线程。 如何以真正异步的方式执行SelectMany


    目前,我最好的解决方案是用Taks.Run包装整个方法实现,问题是是否有更好的方法。

    您可以在
    任务中包装代码。运行
    并返回该任务。这将使一切在后台运行

    以下单元测试显示了该用法:

    [TestClass]
    public class PLinqTests
    {
        private static readonly Stopwatch Watch = Stopwatch.StartNew();
    
        [TestMethod]
        public async Task TestPerformAsync()
        {
            await PerformActionAsync(Enumerable.Range(0, 10));
        }
    
        private Task PerformActionAsync(IEnumerable<int> xIds)
        {
            return Task.Run(() =>
            {
                var affectedYIds = xIds
                    .AsParallel()
                    .WithDegreeOfParallelism(5)
                    .SelectMany(this.GetAffectedYIdsLongRunning)
                    .Distinct();
    
                affectedYIds.ForAll(this.ExcuteLongRunningAction);
            });
        }
    
        private void ExcuteLongRunningAction(int yId)
        {
            Thread.Sleep(1000);
            Console.WriteLine("Executed {0} at {1}.", yId, Watch.Elapsed.Seconds);
        }
    
        private IEnumerable<int> GetAffectedYIdsLongRunning(int xId)
        {
            Thread.Sleep(1000);
            Console.WriteLine("Getting Affected for {0} at {1}.", xId, Watch.Elapsed.Seconds);
    
            return Enumerable.Range(30, 10);
        }
    }
    

    您搜索过
    Parallel.for
    Parallel.Foreach
    以及
    Task.ContinueWith
    ?Parallel.for和Parallel.Foreach阻止调用线程,Task.ContinueWith不支持合并列表。我用更具体的信息更新了这个问题,“阻止调用线程”与“不并行执行”不是一回事
    .aspallel()
    是并行的,但不是异步的。
    getaffectedyidslong本身是否是一个异步操作?如果不是,并行是您将获得的最好结果——您可以推迟使用
    Task.Run()
    获得结果,并声明
    PerformActionAsync
    返回此
    任务。据我所知,直到步骤3,您的性能是I/O绑定的,而不是CPU绑定的。这意味着一个异步解决方案,而不是一个并行、多线程的解决方案。
    
    [TestClass]
    public class PLinqTests
    {
        private static readonly Stopwatch Watch = Stopwatch.StartNew();
    
        [TestMethod]
        public async Task TestPerformAsync()
        {
            await PerformActionAsync(Enumerable.Range(0, 10));
        }
    
        private Task PerformActionAsync(IEnumerable<int> xIds)
        {
            return Task.Run(() =>
            {
                var affectedYIds = xIds
                    .AsParallel()
                    .WithDegreeOfParallelism(5)
                    .SelectMany(this.GetAffectedYIdsLongRunning)
                    .Distinct();
    
                affectedYIds.ForAll(this.ExcuteLongRunningAction);
            });
        }
    
        private void ExcuteLongRunningAction(int yId)
        {
            Thread.Sleep(1000);
            Console.WriteLine("Executed {0} at {1}.", yId, Watch.Elapsed.Seconds);
        }
    
        private IEnumerable<int> GetAffectedYIdsLongRunning(int xId)
        {
            Thread.Sleep(1000);
            Console.WriteLine("Getting Affected for {0} at {1}.", xId, Watch.Elapsed.Seconds);
    
            return Enumerable.Range(30, 10);
        }
    }
    
    Getting Affected for 0 at 1.
    Getting Affected for 1 at 1.
    Getting Affected for 2 at 1.
    Getting Affected for 4 at 2.
    Getting Affected for 3 at 2.
    Getting Affected for 5 at 2.
    Getting Affected for 6 at 2.
    Getting Affected for 7 at 3.
    Getting Affected for 8 at 3.
    Getting Affected for 9 at 3.
    Executed 32 at 3.
    Executed 31 at 3.
    Executed 30 at 4.
    Executed 34 at 4.
    Executed 33 at 4.
    Executed 37 at 4.
    Executed 36 at 4.
    Executed 35 at 5.
    Executed 39 at 5.
    Executed 38 at 5.