我最近已经发布了有关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
,这是一个异步任务。除InsertIntoDatabaseAsync
和InsertIntoDatabaseSync
之外的其他内容。
现在两个调用都需要完全相同的时间才能完成。在我的系统上,他们都需要9秒才能完成。
为了保持TransformBlock
中数据的顺序,我必须将MaxDegreeOfParallelism
限制为1.这样可以保持顺序,但效率没有增加。
如果我尝试增加MaxDegreeOfParallelism
让我们说5,那么完成该过程所需的时间会增加,并且超过25秒。订单也会被打扰。
我正在寻找一种既有秩序又有并行性的方法。如果TPL Dataflow无法实现这一点,那么必须有其他方法。请帮助找到正确的方向。
答案 0 :(得分:1)
你想要的东西没有意义。对于流程的每个部分,如果需要订购,则单个部件永远不能并行运行。当您限制要运行的所有内容时,它必须一次运行一个。这不是TPL Dataflow的问题,这是该要求的问题。
TPL Dataflow可以将流分解为多个部分,每个部分与另一个部分并行运行,并允许每个部分与自身并行运行(使用MaxDegreeOfParallelism > 1
),同时保持流本身顺序。
TPL Dataflow保留块的输入和输出顺序,它不会保留块内的顺序(这是将它们插入数据库的位置)。
因此,如果您希望所有流程都是并行和有序的,则TPL Dataflow无法为您提供帮助,但其他任何内容都无法帮助您。
答案 1 :(得分:0)
同时做很多事情(并行)的诀窍是它们不能以相同的顺序完成。要订购一组东西,该集合需要完整,而不是分成单独的任务。在并行任务全部完成后,您可能需要订购该组。