TPL数据流是并行或有序,但不是两者

时间:2015-08-20 08:09:48

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

我最近已经发布了有关Async,Await,TPL和TPL Dataflow的各种问题。所有这些问题都得到了解答,让我了解了很多。

我开始研究异步编程,因为我想运行我的方法Asynchronous,但有一个问题。我想在异步并行运行任务时维护顺序。将记录插入数据库时​​以及使用TextBox控件在表单上打印时需要维护的顺序。 (这里我有限使用FromCurrentSynchronizationContext,因为我从UI线程访问控件)

我被建议使用TPL Dataflow,因为它被告知它提供了我需要的。经过大量的努力和理解TPL Dataflow如何工作后,我设法创建了一个简单的应用程序,用于比较同步调用和TPL数据流代码之间的性能。

  private void btnStartSync_Click(object sender, EventArgs e)
    {
        Stopwatch watch = new Stopwatch();
        watch.Start();

        try
        {
            txtOutput.Clear();

            for (int i = 1; i <= 200; i++)
            {
                bool x = InsertIntoDatabaseSync(i);

                if (x)
                    txtOutput.Text += "Value Inserted for Id: " + i + Environment.NewLine;
                else
                    txtOutput.Text += "Value Failed for Id: " + i + Environment.NewLine;

                txtOutput.Refresh();
            }

            watch.Stop();
            lblSyncTime.Text = Convert.ToString(watch.ElapsedMilliseconds / 1000);
        }
        catch
        {

        }
    }

上面提到的代码是对同步方法的同步调用,该方法将记录插入数据库中。

 public async void btnTPLDataFlow_Click(object sender, EventArgs e)
    {
        Stopwatch watch = new Stopwatch();
        watch.Start();

        txtOutput.Clear();

        ExecutionDataflowBlockOptions execOptions = new ExecutionDataflowBlockOptions();
        execOptions.MaxDegreeOfParallelism = 1;

        var transformBlock = new TransformBlock<string, OutputPropertyClass>(async v =>
        {
            try
            {
                bool x = await InsertIntoDatabaseAsync(Convert.ToInt32(v));

                OutputPropertyClass objResult = new OutputPropertyClass();
                objResult.Id = Convert.ToInt32(v);
                objResult.result = x;

                return objResult;
            }
            catch
            {
                throw new Exception("Exception occurred for: " + v);
            }

        }, execOptions);


        ActionBlock<OutputPropertyClass> actionBlock = new ActionBlock<OutputPropertyClass>(v =>
        {
            try
            {
                if (v.result)
                    txtOutput.Text += "Value Inserted for Id: " + v.Id  + Environment.NewLine;
                else
                    txtOutput.Text += "Value Failed for Id: " + v.Id + Environment.NewLine;
            }
            catch
            {
                throw new Exception("Exception occurred");
            }                

        }, new ExecutionDataflowBlockOptions { TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext() });


        for (int i = 1; i <= 200; i++)
        {
            transformBlock.Post(i.ToString());
        }

        transformBlock.LinkTo(actionBlock, new DataflowLinkOptions { PropagateCompletion = true });
        transformBlock.Complete();

        try
        {
            await transformBlock.Completion;
        }
        catch (AggregateException ex)
        {
            MessageBox.Show(ex.InnerException.Message);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }


        watch.Stop();
        lblTPLDataFlow.Text = Convert.ToString(watch.ElapsedMilliseconds / 1000);
    }

上面提到的代码是异步调用DataFlow块。 TransformBlock正在调用方法InsertIntoDatabaseAsync,这是一个异步任务。除InsertIntoDatabaseAsyncInsertIntoDatabaseSync之外的其他内容。

现在两个调用都需要完全相同的时间才能完成。在我的系统上,他们都需要9秒才能完成。

为了保持TransformBlock中数据的顺序,我必须将MaxDegreeOfParallelism限制为1.这样可以保持顺序,但效率没有增加。

如果我尝试增加MaxDegreeOfParallelism让我们说5,那么完成该过程所需的时间会增加,并且超过25秒。订单也会被打扰。

我正在寻找一种既有秩序又有并行性的方法。如果TPL Dataflow无法实现这一点,那么必须有其他方法。请帮助找到正确的方向。

2 个答案:

答案 0 :(得分:1)

你想要的东西没有意义。对于流程的每个部分,如果需要订购,则单个部件永远不能并行运行。当您限制要运行的所有内容时,它必须一次运行一个。这不是TPL Dataflow的问题,这是该要求的问题。

TPL Dataflow可以将流分解为多个部分,每个部分与另一个部分并行运行,并允许每个部分与自身并行运行(使用MaxDegreeOfParallelism > 1),同时保持流本身顺序。

TPL Dataflow保留块的输入和输出顺序,它不会保留块内的顺序(这是将它们插入数据库的位置)。

因此,如果您希望所有流程都是并行和有序的,则TPL Dataflow无法为您提供帮助,但其他任何内容都无法帮助您。

答案 1 :(得分:0)

同时做很多事情(并行)的诀窍是它们不能以相同的顺序完成。要订购一组东西,该集合需要完整,而不是分成单独的任务。在并行任务全部完成后,您可能需要订购该组。