C# 第三方物流数据流既可以是并行的,也可以是有序的,但不能两者兼而有之
最近我已经发布了关于Async、Wait、TPL和TPL数据流的各种问题。所有这些问题的回答让我明白了很多 我开始研究异步编程,因为我想异步运行我的方法,但有一个陷阱。我希望在任务异步并行运行时保持顺序。将记录插入数据库以及使用C# 第三方物流数据流既可以是并行的,也可以是有序的,但不能两者兼而有之,c#,.net,task-parallel-library,tpl-dataflow,C#,.net,Task Parallel Library,Tpl Dataflow,最近我已经发布了关于Async、Wait、TPL和TPL数据流的各种问题。所有这些问题的回答让我明白了很多 我开始研究异步编程,因为我想异步运行我的方法,但有一个陷阱。我希望在任务异步并行运行时保持顺序。将记录插入数据库以及使用文本框控件在表单上打印记录时需要维护的顺序。(这里我必须使用CurrentSynchronizationContext中的FromCurrentSynchronizationContext,因为我是从UI线程访问控件的) 有人建议我使用TPL数据流,因为有人告诉我它提供了
文本框
控件在表单上打印记录时需要维护的顺序。(这里我必须使用CurrentSynchronizationContext中的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
{
}
}
private void btnStartSync_单击(对象发送方,事件参数e)
{
秒表=新秒表();
watch.Start();
尝试
{
txtOutput.Clear();
对于(int i=1;i
{
尝试
{
boolx=await InsertIntoDatabaseAsync(Convert.ToInt32(v));
OutputPropertyClass objResult=新的OutputPropertyClass();
objResult.Id=Convert.ToInt32(v);
objResult.result=x;
返回objResult;
}
抓住
{
抛出新异常(“异常发生于:“+v”);
}
},可供选择);
ActionBlock ActionBlock=新的ActionBlock(v=>
{
尝试
{
如果(v.结果)
txtOutput.Text+=“为Id插入的值:”+v.Id+Environment.NewLine;
其他的
对于Id:+v.Id+Environment.NewLine,txtOutput.Text+=“值失败;
}
抓住
{
抛出新异常(“发生异常”);
}
},新的ExecutionDataflowBlockOptions{TaskScheduler=TaskScheduler.FromCurrentSynchronizationContext()});
对于(inti=1;i同时做很多事情的技巧(并行)它们不会以相同的顺序完成。要订购一组东西,该集合需要完整,而不是分解为单独的任务。您可能需要在并行任务全部完成后订购该集合。您想要的没有意义。对于流程的每个部分,如果需要订购,则单个部分永远不能并行运行。当您将所有内容限制为按顺序运行时,它必须一次运行一个。这不是TPL数据流的问题,而是需求的问题
TPL数据流允许将流拆分为多个部分,每个部分彼此并行运行,并允许每个部分与自身并行运行(使用MaxDegreeOfParallelism>1
),同时保持流本身的有序
TPL数据流保持块的输入和输出顺序,而不保持块内部的顺序(在块内部,您可以将它们插入数据库)
因此,如果您希望所有流都是并行且有序的,那么TPL数据流无法帮助您,但其他任何方法都无法帮助您。感谢您的快速回复。例如,我有单独的方法来执行单个任务。在这种情况下,我应该将它们作为一种方法吗?以及如何将UI文本更新与插入方法相结合。UI控件处于单独状态e线程。这只是我第一次为TPL数据流编写的演示代码。我知道这个场景没有达到标准,但我按照我的理解测试了性能。好吧,我放弃了在块内保持秩序的要求,我不需要有序的记录。既然如此,我可以保持秩序吗文本框上打印的值的r?@AdnanYaseen是的,您的代码已经这样做了,因为TransfromBlock
以正确的顺序输出项,并且您的ActionBlock
是单线程的(这就是为什么我首先建议这样做的原因)。谢谢。简单一点。TPL数据流是否存在资源锁定或争用问题?我是否需要处理此问题,或者这不是问题。如果这不是一个相关问题,我很抱歉。@AdnanYaseen如果块内的代码并行运行,则它需要与任何其他代码一样的保护和同步。除此之外,您不需要您可以使用多个线程对块进行写入和读取。您可能应该等待actionBlock.Completion
而不是等待transformBlock.Completion
,因为管道中的最后一个块是actionBlock
。
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);
}