【问题标题】:PLINQ inside task sometime slowerPLINQ 内部任务有时较慢
【发布时间】:2018-03-16 11:22:39
【问题描述】:

我正在研究一些文本解析器逻辑,并进行 IO 和 CPU 绑定操作。 一般来说,我做什么:读取文件 -> 处理文本,读取文件 2 -> 处理文本 2... 等等。

我可以做的是让操作“处理文本”和“读取文件 2”在不同的任务中同时执行。然而,当我创建相关任务树时,我注意到 PLINQ 变得比最初慢。这是代码的简化示例:

类程序 { 静态 int idx;

public static void Main()
{
    idx = 0;
    Stopwatch sw = new Stopwatch();
    sw.Start();

    for (int i = 0; i < 10; i++)
    {
        // here I execute my function with PLINQ
        FuncP();
    }

    sw.Stop();

    Console.WriteLine(sw.ElapsedMilliseconds);

    idx = 0;

    sw.Start();

    List<Task> tasks = new List<Task>();
    for (int i = 0; i < 10; i++)
    {
        int iLocal = i;
        Task tsk;
        if (i > 0)
        {
            // next task depends on previous one
            tsk = Task.Run(() =>
            {
                Task.WaitAll(tasks[iLocal - 1]);

                // execute the same function
                FuncP();
            });
        }
        else // i = 0
        {
            // first task does not depend on other tasks
            tsk = Task.Run(() =>
                {
                    // execute the same function
                    FuncP();
                });
        }

        tasks.Add(tsk);
    }

    tasks.Last().Wait();
    sw.Stop();

    Console.WriteLine(sw.ElapsedMilliseconds);
}

private static void FuncP()
{
    Stopwatch sw1 = new Stopwatch();
    sw1.Start();

    Console.WriteLine(string.Format("FuncP start {0}", ++idx));
    string s = new string('c', 2000);
    s.AsParallel()
        .ForAll(_ =>
        {
            for (int i = 0; i < 1000000; i++) ;
        });

    sw1.Stop();

    Console.WriteLine(string.Format("FuncP end {0}, Elapsed {1}", idx, sw1.ElapsedMilliseconds));
}

}

输出是:

FuncP start 1
FuncP end 1, Elapsed 409
FuncP start 2
FuncP end 2, Elapsed 345
FuncP start 3
FuncP end 3, Elapsed 344
FuncP start 4
FuncP end 4, Elapsed 337
FuncP start 5
FuncP end 5, Elapsed 344
FuncP start 6
FuncP end 6, Elapsed 343
FuncP start 7
FuncP end 7, Elapsed 348
FuncP start 8
FuncP end 8, Elapsed 351
FuncP start 9
FuncP end 9, Elapsed 343
FuncP start 10
FuncP end 10, Elapsed 334
3504
FuncP start 1
FuncP end 1, Elapsed 5522 --> here is high execution time
FuncP start 2
FuncP end 2, Elapsed 368
FuncP start 3
FuncP end 3, Elapsed 340
FuncP start 4
FuncP end 4, Elapsed 347
FuncP start 5
FuncP end 5, Elapsed 351
FuncP start 6
FuncP end 6, Elapsed 347
FuncP start 7
FuncP end 7, Elapsed 353
FuncP start 8
FuncP end 8, Elapsed 337
FuncP start 9
FuncP end 9, Elapsed 341
FuncP start 10
FuncP end 10, Elapsed 345
12160

有时它会在任务中第一次运行 PLINQ 时挂起,有时在第二次运行时会挂起,但是对于任务,它在循环中花费的时间要多得多。 不确定我是否完全理解 FuncP 执行时间如此之高的原因,是不是 AsParallel() 由于线程池中缺少“空闲”线程而无法并行?
有人可以解释吗?提前致谢。

【问题讨论】:

  • 整个方法似乎有问题(例如,Task.WaitAll(tasks[iLocal - 1]); 的使用不是线程安全的,因为可能同时向tasks 添加不同的线程)。 docs.microsoft.com/en-us/dotnet/standard/parallel-programming/… 可能值得考虑作为替代方案。或者,如果您不想了解这一点,请将read file 添加到BlockingCollectionprocess text 从不同的线程/任务中读取。见stackoverflow.com/questions/6608042/…
  • @mjwills 当然,我明白这一切,只是粗略的代码示例,主要对时间原因感兴趣,谢谢

标签: c# parallel-processing


【解决方案1】:

这个问题可能是由于线程池有多少空闲线程而导致的。 Matt Warren 的 article 值得一读。

如果您将 ThreadPool 设置为具有 最小 线程数,例如使用:

ThreadPool.SetMinThreads(100, 100);

那么这两个代码示例的行为大致相同,因为“备用”线程可用。

请注意,我不建议在生产中使用 100 - 这只是一个显示对比的示例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-22
    • 1970-01-01
    相关资源
    最近更新 更多