ConcurrentQueue上的PLINQ不是多线程的

时间:2011-05-22 04:45:35

标签: c# multithreading postgresql plinq npgsql

我在C#程序中有以下PLINQ语句:

 foreach (ArrestRecord arrest in
            from row in arrestQueue.AsParallel()
            select row)
        {
            Geocoder geocodeThis = new Geocoder(arrest);
            writeQueue.Enqueue(geocodeThis.Geocode());
            Console.Out.WriteLine("Enqueued " + ++k);
        }

arrestQueuewriteQueue都是ConcurrentQueues

没有任何东西并行运行:

  • 在运行时,总CPU使用率约为30%,而且其他所有内容都在运行。我有8个内核(Core i7 720QM上有超线程,有4个物理内核),8个内核中有4个几乎没有任何利用率。其余的大约40%-50%。
  • 磁盘使用率通常为0%,除了查询localhost上的Postgres数据库之外没有网络使用情况(见下文)。
  • 如果我在geocodeThis.Geocode()内的某处添加断点,则Visual Studio的线程下拉列表会显示 [ pid ]主线程。它永远不会进入任何其他线程。
  • 我使用Npgsql连接到Postgres,每个线程对表运行一些 SELECT 查询。我正在运行pgAdmin III的服务器状态应用程序,该应用程序显示 pg_stat_activity 。通过监视这个和战略断点位置(见上文),我可以看到app never 为所有运行geocodeThis.Geocode()的所谓并发线程打开了多个数据库连接。即使我将 Pooling = false 添加到数据库连接字符串中,为了强制连接不被合并,我也从未在geocodeThis.Geocode()中看到多于1个连接。
  • Postgres表在 WHERE 子句中的每一列上编制索引。即使索引编制很差,我也希望有大量的磁盘使用率。如果Postgres以任何其他方式举起东西,看起来它会沉浸在核心之中。

这看起来像是一个简单的PLINQ案例研究,我为什么没有并行运行而感到头疼。

2 个答案:

答案 0 :(得分:5)

您只是对assertQueue本身的枚举进行并行化,然后将其“平行化”回到普通的IEnumerable。这一切都发生在foreach循环开始之前。然后你使用普通的IEnumerableforeach串行运行循环体。

有许多方法可以并行运行循环体,但首先想到的是使用Parallel.ForEach

Parallel.ForEach(arrestQueue, arrest =>
    {
        Geocoder geocodeThis = new Geocoder(arrest);
        writeQueue.Enqueue(geocodeThis.Geocode());
        Console.Out.WriteLine("Enqueued " + ++k);
    });

答案 1 :(得分:1)

对并行集合的Foreach仍然是单线程操作。 .AsParallel返回一个集合,该集合定义了一个.ForAll方法,该方法可能(但通过契约并不总是)并行运行。代码将是:

arrestQueue.AsParallel().ForAll(arrest=>
    {
        Geocoder geocodeThis = new Geocoder(arrest);
        writeQueue.Enqueue(geocodeThis.Geocode());
        Console.Out.WriteLine("Enqueued " + ++k);
    });