AsParallel()按顺序执行

时间:2014-10-06 06:23:05

标签: c# .net linq task-parallel-library plinq

我有以下PLINQ查询:

// Let's get a few customers
List<Customer> customers = CustomerRepository.GetSomeCustomers();

// Let's get all of the items for all of these customers
List<CustomerItem> items = customers
    .AsParallel()
    .SelectMany(x => ItemRepository.GetItemsByCustomer(x))
    .ToList();

我希望GetItemsByCustomer()可以为每个客户并行执行,但是按顺序执行

我试图强迫并行,但仍然没有运气:

List<CustomerItem> items = customers
    .AsParallel()
    .WithExecutionMode(ParallelExecutionMode.ForceParallelism)
    .SelectMany(x => ItemRepository.GetItemsByCustomer(x))
    .ToList();

方法签名:

private IEnumerable<Item> GetItemsByCustomer(Customer customer)
{
    // Get all items for a customer...
}

根据this article,如果PLINQ认为合适,它肯定可以采用顺序路由,但强制并行应该仍然会覆盖它。

注意:上面的示例纯粹是说明性的 - 假设customers是一个小列表而GetItemsByCustomer是一种昂贵的方法。

1 个答案:

答案 0 :(得分:5)

AsParallel()没有错。如果可能的话,它将以并行方式运行,并且LINQ表达式中没有顺序依赖,因此没有任何东西可以强制它顺序运行。

您的代码不能并行运行的几个原因可能是:

  1. 您的box / vm只有一个CPU,或者您有一个.NET设置来将并行性限制为一个CPU。您可以使用以下代码模拟它:

          var customers = new List<Customer>() { new Customer() {Name = "Mick", Surname = "Jagger"}, new Customer() {Name = "George", Surname = "Clooney"},new Customer() {Name = "Kirk", Surname = "DOuglas"}};
    
          var items = customers
            .AsParallel()
            .SelectMany(x =>
            {
                 Console.WriteLine("Requesting: " + x.Name + " - " + DateTime.Now);
                 Thread.Sleep(3000);
                 return new List<CustomerItem>();
    
            })
            .WithDegreeOfParallelism(1)
            .ToList();
    

    即使您在单个核心/ CPU盒上强制使用WithExecutionMode(ParallelExecutionMode.ForceParallelism)进行并行操作,或者当并行度为1时,您的设置也无效,因为无法实现真正​​的并行性。

  2. 存储库中发生的共享资源存在一些线程锁定。您可以使用以下代码模拟线程锁定:

        var customers = new List<Customer>() { new Customer() {Name = "Mick", Surname = "Jagger"}, new Customer() {Name = "George", Surname = "Clooney"},new Customer() {Name = "Kirk", Surname = "DOuglas"}};
    
        var locker = new object();
    
        // Let's get all of the items for all of these customers
        var items = customers
            .AsParallel()
            .SelectMany(x =>
            {
                lock (locker)
                {
                    Console.WriteLine("Requesting: " + x.Name + " - " + DateTime.Now);
                    Thread.Sleep(3000);
                    return new List<CustomerItem>();
                }
    
            })
            .ToList();
    
  3. 在某些情况下,有一些数据库设置强制查询/读取是连续的,这可能会让您觉得您的C#代码并非并行运行,而实际上是。