TPL并行如何工作?

时间:2017-09-11 11:27:06

标签: .net task-parallel-library tpl-dataflow

我正在尝试使用TPL块,并且遇到了令我困惑的地方。 TPL块应并行运行,具体取决于我的设置(MaxDegreeOfParallelism)。

我做了什么?

我创建了5个块:

  • 第1区块需要3秒钟才能完成

  • 第2块需要1秒

  • ......休息在几毫秒内完成

所有人都将MaxDegreeOfParallelism设置为32(Environment.ProcessorCount X 4),EnsureOrdered设置为false。一切都很好,每件事都是平行的。我假设这是我期望的最大吞吐量。我在我的块链中异步发送50条消息。一个简单的for循环递增整数值。

现在,我对Block 1进行了一次更改。我在ExecutionDataflowBlockOptions

中添加了一个设置

TaskScheduler = new LimitedConcurrencyLevelTaskScheduler(2)

现在,此设置仅限我的Block 1为两个线程。只有并行运行时才能获得最多两个线程。这是代码:

class Example1
{
    static void Main(string[] args)
    {
        Func<string, string> f = (string uri) =>
        {
            Thread.Sleep(3000);
            Console.WriteLine(DateTime.Now.ToString() + $">>[{AppDomain.GetCurrentThreadId()}] block1...({uri})");
            return uri.ToString(); ;
        };
        var block1 = new TransformBlock<string, string>(f, new ExecutionDataflowBlockOptions
        {
            EnsureOrdered = false,
            MaxDegreeOfParallelism = 32,
            TaskScheduler = new LimitedConcurrencyLevelTaskScheduler(2)
        });

        var block2 = new TransformBlock<string, string[]>(text =>
        {
            Thread.Sleep(1000);
            Console.WriteLine(DateTime.Now.ToString() + $">>[{AppDomain.GetCurrentThreadId()}] block2...({text})");
            var retVal = new string[1];
            retVal[0] = text;
            return retVal;
        }, new ExecutionDataflowBlockOptions
        {
            EnsureOrdered = false,
            MaxDegreeOfParallelism = 32,
        });

        var block3 = new TransformBlock<string[], string[]>(words =>
        {
            Console.WriteLine(DateTime.Now.ToString() + $">>[{AppDomain.GetCurrentThreadId()}] block3..({words[0]})");
            return words;
        }, new ExecutionDataflowBlockOptions
        {
            EnsureOrdered = false,
            MaxDegreeOfParallelism = 32
        });

        var block4 = new TransformManyBlock<string[], string>(words =>
        {
            Console.WriteLine(DateTime.Now.ToString() + $">>[{AppDomain.GetCurrentThreadId()}] block4...({words[0]})");
            var retVal = new ConcurrentQueue<string>();
            retVal.Enqueue(words[0]);
            return retVal;
        }, new ExecutionDataflowBlockOptions
        {
            EnsureOrdered = false,
            MaxDegreeOfParallelism = 32
        });

        var block5 = new ActionBlock<string>(reversedWord =>
        {
            Console.WriteLine(DateTime.Now.ToString() + $">>[{AppDomain.GetCurrentThreadId()}] block5...({reversedWord[0]})");
        }, new ExecutionDataflowBlockOptions
        {
            EnsureOrdered = false,
            MaxDegreeOfParallelism = 32
        });

        block1.LinkTo(block2);
        block2.LinkTo(block3);
        block3.LinkTo(block4);
        block4.LinkTo(block5);


        block1.Completion.ContinueWith(t =>
        {
            if (t.IsFaulted) ((IDataflowBlock)block2).Fault(t.Exception);
            else block2.Complete();
        });
        block2.Completion.ContinueWith(t =>
        {
            if (t.IsFaulted) ((IDataflowBlock)block3).Fault(t.Exception);
            else block3.Complete();
        });
        block3.Completion.ContinueWith(t =>
        {
            if (t.IsFaulted) ((IDataflowBlock)block4).Fault(t.Exception);
            else block4.Complete();
        });
        block4.Completion.ContinueWith(t =>
        {
            if (t.IsFaulted) ((IDataflowBlock)block5).Fault(t.Exception);
            else block5.Complete();
        });

        var d1 = DateTime.Now;
        for (int i = 0; i < 50; i++)
        {
            block1.SendAsync(i.ToString());
        }


        block1.Complete();
        block5.Completion.Wait();
        var d2 = DateTime.Now;
        Console.WriteLine($"Time taken {(d2.Ticks - d1.Ticks) / 10000000} seconds");
        Console.WriteLine("DONE...");
        Console.ReadLine();
    }

当我考虑运行此代码时,

(t1,t2表示线程)

(B1(1)表示执行消息1的块1)

(B2(1)表示执行消息1的块2)

(B1(2)表示执行消息2的块1)

(T1,T2代表时间单位)

        T1          T2
t1      B1(1)       B1(3)...

t2      B1(2)       B1(4)...

t3                  B2(1)

t4                  B2(2)

在时间段T2中,我的B2块将开始运行。但事实并非如此:

9/11/2017 4:48:55 PM>>[1240] block1...(1)
9/11/2017 4:48:55 PM>>[9688] block1...(0)
9/11/2017 4:48:58 PM>>[9688] block1...(3)
9/11/2017 4:48:58 PM>>[1240] block1...(2)
9/11/2017 4:49:01 PM>>[1240] block1...(5)
9/11/2017 4:49:01 PM>>[9688] block1...(4)
9/11/2017 4:49:04 PM>>[1240] block1...(6)
9/11/2017 4:49:04 PM>>[9688] block1...(7)
9/11/2017 4:49:07 PM>>[9688] block1...(9)
9/11/2017 4:49:07 PM>>[1240] block1...(8)
9/11/2017 4:49:10 PM>>[1240] block1...(11)
9/11/2017 4:49:10 PM>>[9688] block1...(10)
9/11/2017 4:49:13 PM>>[9688] block1...(13)
9/11/2017 4:49:13 PM>>[1240] block1...(12)
9/11/2017 4:49:16 PM>>[1240] block1...(15)
9/11/2017 4:49:16 PM>>[9688] block1...(14)

首先为所有消息运行Block 1,然后启动Block 2.

我在这里做错了吗?

或者我的期望是不正确的TPL块(管道和过滤器模式)?

0 个答案:

没有答案