我一直在尝试通过创建一个示例应用程序来理解TPL Dataflow。其中一个我一直在尝试做的事情是从ActionBlock更新TextBox控件。使用TPL Dataflow的原因是在保持顺序的同时执行并行异步操作。以下函数由我编写:
private TaskScheduler scheduler = null;
public Form1()
{
this.scheduler = TaskScheduler.FromCurrentSynchronizationContext();
InitializeComponent();
}
public async void btnTPLDataFlow_Click(object sender, EventArgs e)
{
Stopwatch watch = new Stopwatch();
watch.Start();
txtOutput.Clear();
ExecutionDataflowBlockOptions execOptions = new ExecutionDataflowBlockOptions();
execOptions.MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded;
execOptions.TaskScheduler = scheduler;
ActionBlock<int> actionBlock = new ActionBlock<int>(async v =>
{
bool x = await InsertIntoDatabaseAsync(v);
if (x)
txtOutput.Text += "Value Inserted for: " + v + Environment.NewLine;
else
txtOutput.Text += "Value Failed for: " + v + Environment.NewLine;
}, execOptions);
for (int i = 1; i <= 200; i++)
{
actionBlock.Post(i);
}
actionBlock.Complete();
await actionBlock.Completion;
watch.Stop();
lblTPLDataFlow.Text = Convert.ToString(watch.ElapsedMilliseconds / 1000);
}
private async Task<bool> InsertIntoDatabaseAsync(int id)
{
try
{
string connString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\\TPLDatabase.accdb;Persist Security Info=False;";
using (OleDbConnection conn = new OleDbConnection(connString))
{
string commandText = "INSERT INTO tblRecords (ProductName, ProductDescription, IsProcessed) VALUES (@ProductName, @ProductDescription, @IsProcessed)";
await conn.OpenAsync();
using (OleDbCommand command = new OleDbCommand(commandText, conn))
{
command.CommandType = CommandType.Text;
command.Parameters.AddWithValue("@ProductName", "Product " + id);
command.Parameters.AddWithValue("@ProductDescription", "Description " + id);
command.Parameters.AddWithValue("@IsProcessed", false);
if (await command.ExecuteNonQueryAsync() > 0)
return true;
else
return false;
}
}
}
catch
{
return false;
}
}
现在上面的代码可以正常运行。它按顺序将记录插入到我的样本MS Access数据库中,并按顺序更新UI。但是,这个问题是它会阻塞UI,这是可以理解的,因为我正在使用
TaskScheduler.FromCurrentSynchronizationContext
,它将在UI线程上更新TextBox
。我对代码进行了小改动,并从
ExecutionDataflowBlockOptions
中删除了调度程序。相反,我使用以下代码更新UI:txtOutput.Invoke(new MethodInvoker(delegate
{
if (x)
txtOutput.Text += "Value Inserted for: " + v + Environment.NewLine;
else
txtOutput.Text += "Value Failed for: " + v + Environment.NewLine;
}));
现在这个更改不会冻结用户界面,但是数据库中的值顺序和文本框中显示的值顺序都受到了严重干扰。新的顺序如下:
ID ProductName ProductDescription IsProcessed
6847 Product 6 Description 6 False
6848 Product 7 Description 7 False
6849 Product 8 Description 8 False
6850 Product 10 Description 10 False
6851 Product 11 Description 11 False
6852 Product 12 Description 12 False
6853 Product 9 Description 9 False
6854 Product 13 Description 13 False
6855 Product 14 Description 14 False
现在,在我的情况下,更新UI并保持顺序的最佳方法是什么?
PropagateCompletion
。 - i3arnon