理解 AsParallel 和 AsSequential:LINQ 查询的哪一部分是并发的? [英] Understanding AsParallel and AsSequential: which part of the LINQ query is concurrent?

查看:42
本文介绍了理解 AsParallel 和 AsSequential:LINQ 查询的哪一部分是并发的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图了解是否可以使用留给 AsParallel 查询的非线程安全类.类似的东西:

src.Select(item => nonSafeClass.Process(item)).AsParallel().Select(item => DoComputationalIntenseButThreadSafeWork(item));

我尝试运行以下代码以查看查询链的哪一部分并行执行,哪些不是:

IEnumerable数组 = Enumerable.Range(0, short.MaxValue).ToArray();array.Select(i =>{Console.WriteLine("第一步:{0}", Thread.CurrentThread.ManagedThreadId);返回我;}).AsParallel().Select(i =>{Console.WriteLine("第二步:{0}", Thread.CurrentThread.ManagedThreadId);返回我;}).AsSequential().Select(i =>{Console.WriteLine("第三步:{0}", Thread.CurrentThread.ManagedThreadId);返回我;}).ToList();

但令我惊讶的是,第一步"和第三步"出现在不同的线程 ID 上.我期待只为第二步"看到不同的线程 ID,因为它介于 AsParallelAsSequential 之间.我的想法错了吗?

解决方案

这是因为 延迟执行.

以及Linq 中的链式查询的工作原理.

如果换成最简单的情况

array.Select(i =>{Console.WriteLine("第一步:{0}", Thread.CurrentThread.ManagedThreadId);返回我;}).Select(i =>{Console.WriteLine("第二步:{0}", Thread.CurrentThread.ManagedThreadId);返回我;}).Select(i =>{Console.WriteLine("第三步:{0}", Thread.CurrentThread.ManagedThreadId);返回我;}).ToList();

你会看到:第1步第2步第 3 步第1步第2步第 3 步......

现在想象一下你的假设是否正确:

First Select() 在 Thread 1(主线程)上运行.然后您的 AsParallel 在不同的线程上运行,但最后,您的最终 AsSequential() 需要在同一线程上运行,这意味着具有 AsParallel 在不同的线程上运行,因为 Thread 1 被阻塞了.

你想的流程是:<代码>1 ->x ->1

紧随其后<代码>1 ->y ->1

等等等等.

作为一种优化,当 Linq 检测到您有一个 select 后跟 AsParallel 时,它会在单独的线程上为每次迭代运行它们.同样,这是因为从 1 ->x ->1 ->y 不会让任何东西并行"运行.

通过运行简化版本来尝试:

array.Select(i =>{Console.WriteLine("第一步:{0}", Thread.CurrentThread.ManagedThreadId);返回我;}).AsParallel().Select(i =>{Console.WriteLine("第二步:{0}", Thread.CurrentThread.ManagedThreadId);返回我;}).ToList();

您将看到第 1 步和第 2 步按顺序"进行,但每次迭代都在不同的线程上完成.

然而,您的 AsSequential() 将在执行它的主线程上运行.

因此,我希望第 1 步和第 2 步在与调用线程不同的同一线程上运行,但第 3 步在启动链的同一线程上运行.>

如果您想实现您所描述的行为,只需将您的查询更改为:

array.Select(i =>{Console.WriteLine("第一步:{0}", Thread.CurrentThread.ManagedThreadId);返回我;}).ToList().AsParallel().Select(i =>{Console.WriteLine("第二步:{0}", Thread.CurrentThread.ManagedThreadId);返回我;}).AsSequential().Select(i =>{Console.WriteLine("第三步:{0}", Thread.CurrentThread.ManagedThreadId);返回我;}).ToList();

第一个 ToList() 评估将在调用线程上运行所有内容,然后 AsParallel() 在不同线程上运行每次迭代(取决于 ThreadPool 可用性),最后,您的 AsSequential 将确保顺序位在调用线程.

I'm trying to understand if I can use a non thread-safe class left to a AsParallel query. Something like:

src.Select(item => nonSafeClass.Process(item))
   .AsParallel()
   .Select(item => DoComputationalIntenseButThreadSafeWork(item));

I've tried to run the following code to see which part of the query chain is executed in parallel in which is not:

IEnumerable<int> array = Enumerable.Range(0, short.MaxValue).ToArray();
array.Select(i =>
    {
        Console.WriteLine("Step One: {0}", Thread.CurrentThread.ManagedThreadId);
        return i;
    }).AsParallel().Select(i =>
        {
            Console.WriteLine("Step Two: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;

        }).AsSequential().Select(i =>
            {
                Console.WriteLine("Step Three: {0}", Thread.CurrentThread.ManagedThreadId);
                return i;
            }).ToList();

But to my surprise, both "Step One" and "Step Three" appear on different thread IDs. I was expecting to see different thread IDs only for "Step Two" because it is between a AsParallel and a AsSequential. Is my thinking wrong?

解决方案

This is because of deferred execution.

And how chained queries in Linq work.

If you change it to the simplest case of

array.Select(i =>
        {
            Console.WriteLine("Step One: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;
        }).Select(i =>
        {
            Console.WriteLine("Step Two: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;

        }).Select(i =>
        {
            Console.WriteLine("Step Three: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;
        }).ToList();

You will see this: Step 1 Step 2 Step 3 Step 1 Step 2 Step 3 ... ...

Now imagine if your assumption was correct:

First Select() runs on Thread 1 (main thread). Then your AsParallel runs on a different thread but finally, your final AsSequential() needs to run on the same thread, meaning it doesn't make any difference to have the AsParallel running on a different thread since Thread 1 is blocked.

The flow you were thinking would be: 1 -> x -> 1

followed by 1 -> y -> 1

and so on and so forth.

As an optimization, when Linq detects that you have a select followed by AsParallel, it runs them for each iteration on a separate thread. Again, this is because going from 1 -> x -> 1 -> y wouldn't make anything run in "parallel".

Try it by running a simplified version:

array.Select(i =>
        {
            Console.WriteLine("Step One: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;
        }).AsParallel().Select(i =>
        {
            Console.WriteLine("Step Two: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;

        }).ToList();

You will see that Step 1 and Step 2 are don in "sequence" but each iteration is done on a different thread.

Your AsSequential() however will run on the main thread that executed it.

As such, I would expect Step 1 and Step 2 to run on the same thread which is different from the calling thread but Step 3 to run on the same thread that started the chain.

If you want to achieve the behavior you described, simply change your query to this:

array.Select(i =>
        {
            Console.WriteLine("Step One: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;
        }).ToList().AsParallel().Select(i =>
        {
            Console.WriteLine("Step Two: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;

        }).AsSequential().Select(i =>
        {
            Console.WriteLine("Step Three: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;
        }).ToList();

The first ToList() evaluation will run everything on the calling thread, the AsParallel() then runs each iteration on a different thread (subject to ThreadPool availability) and finally, your AsSequential will make sure the sequential bits are run on the calling thread.

这篇关于理解 AsParallel 和 AsSequential:LINQ 查询的哪一部分是并发的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆