【发布时间】:2015-11-13 18:41:56
【问题描述】:
我最近已经发布了关于 Async、Await、TPL 和 TPL 数据流的各种问题。所有这些问题都得到了解答,让我明白了很多。
我开始研究异步编程,因为我想运行我的异步方法,但有一个问题。我想在任务异步并行运行时保持顺序。记录插入数据库时以及使用TextBox 控件在表单上打印时需要维护的顺序。 (这里我必须使用FromCurrentSynchronizationContext,因为我是从UI线程访问控件)
有人建议我使用 TPL 数据流,因为有人告诉我它提供了我需要的功能。经过一番努力并了解了 TPL 数据流的工作原理后,我设法创建了一个简单的应用程序,用于比较同步调用和 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);
}
上面提到的代码是一个Async调用DataFlow块。 TransformBlock 正在调用 InsertIntoDatabaseAsync 方法,这是一个异步任务。除此之外InsertIntoDatabaseAsync 和InsertIntoDatabaseSync 是相同的。
现在两个调用的完成时间完全相同。在我的系统上,它们都需要 9 秒才能完成。
为了保持TransformBlock 中数据的顺序,我必须将MaxDegreeOfParallelism 限制为1。这样可以保持顺序,但效率没有提高。
如果我尝试增加 MaxDegreeOfParallelism 让我们说 5,那么完成该过程所需的时间会增加,并且超过 25 秒。秩序也受到干扰。
我正在寻找一种同时具有顺序和并行性的方法。如果 TPL Dataflow 无法实现这一点,那么必须有其他方法。请帮助找到正确的方向。
【问题讨论】:
-
您可能应该
await actionBlock.Completion而不是await transformBlock.Completion,因为管道中的最后一个块是actionBlock。
标签: c# .net task-parallel-library tpl-dataflow