正在从 ActionBlock 更新 UI 控件
Updating UI Control from ActionBlock
我一直在尝试通过创建示例应用程序来理解 TPL 数据流。我一直在尝试做的一件事是从 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;
}));
现在,此更改不会冻结 UI,但数据库中值的顺序和 TextBox 中显示的值的顺序受到严重干扰。新订单是这样的,
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 并保持顺序的最佳方法是什么。
TPL 数据流块在输出时保留输入的顺序。它们不会在块内保留执行顺序,这就是为什么你会看到一切都是乱序的。
您可能想要做的是将 ActionBlock
替换为 TransformBlock
以实际同时进行艰苦的工作,并将 link 替换为 ActionBlock
以更新UI一次一个。
你也可以在 UI 线程上有这个块 运行 这样你就不需要使用 Invoke
:
var transformBlock = new TransformBlock<int, int>(
v => InsertIntoDatabaseAsync(v),
execOptions);
var actionBlock = new ActionBlock<int>(x =>
{
if (x)
txtOutput.Text += "Value Inserted for: " + v + Environment.NewLine;
else
txtOutput.Text += "Value Failed for: " + v + Environment.NewLine;
}, new ExecutionDataflowBlockOptions { TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext() })
transformBlock.LinkTo(ActionBlock, new DataflowLinkOptions { PropagateCompletion = true } );
我一直在尝试通过创建示例应用程序来理解 TPL 数据流。我一直在尝试做的一件事是从 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;
}));
现在,此更改不会冻结 UI,但数据库中值的顺序和 TextBox 中显示的值的顺序受到严重干扰。新订单是这样的,
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 并保持顺序的最佳方法是什么。
TPL 数据流块在输出时保留输入的顺序。它们不会在块内保留执行顺序,这就是为什么你会看到一切都是乱序的。
您可能想要做的是将 ActionBlock
替换为 TransformBlock
以实际同时进行艰苦的工作,并将 link 替换为 ActionBlock
以更新UI一次一个。
你也可以在 UI 线程上有这个块 运行 这样你就不需要使用 Invoke
:
var transformBlock = new TransformBlock<int, int>(
v => InsertIntoDatabaseAsync(v),
execOptions);
var actionBlock = new ActionBlock<int>(x =>
{
if (x)
txtOutput.Text += "Value Inserted for: " + v + Environment.NewLine;
else
txtOutput.Text += "Value Failed for: " + v + Environment.NewLine;
}, new ExecutionDataflowBlockOptions { TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext() })
transformBlock.LinkTo(ActionBlock, new DataflowLinkOptions { PropagateCompletion = true } );