从 ActionBlock 更新 UI 控件 [英] Updating UI Control from ActionBlock

查看:41
本文介绍了从 ActionBlock 更新 UI 控件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直试图通过创建示例应用程序来理解 TPL 数据流.我一直在尝试做的一件事是从 ActionBlock 更新一个 TextBox 控件.使用 TPL Dataflow 的原因是在保持顺序的同时执行并行异步操作.下面的函数是我写的,

I have been trying to understand the TPL Dataflow by creating a sample application. One of thing that I have been trying to do is to update a TextBox control from ActionBlock. The reason for using TPL Dataflow is to perform parallel asynchronous operation while keeping the order. The following function is written by me,

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.

Now the above piece of code runs just fine. It inserts the records in order in my sample MS Access database and updates the UI in order also. But the problem with this is that it blocks the UI which is understandable as I am using the TaskScheduler.FromCurrentSynchronizationContext which will be updating TextBox on UI Thread.

我对代码做了一个小改动,并从 ExecutionDataflowBlockOptions 中删除了调度程序.相反,我使用以下代码更新 UI,

I made a small change in the code and remove the scheduler from the ExecutionDataflowBlockOptions. Instead I update the UI with the following code,

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 中显示的值顺序受到严重干扰.新订单是这样的,

Now this change does not freezes the UI but the order of the values in the database and order of values displayed in TextBox is disturbed badly. The new order is like this,

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 并保持顺序的最佳方法是什么.

Now what can be the best way to update the UI in my scenario and also keeping the order.

推荐答案

TPL 数据流块在输出时保留输入的顺序.它们不会在块内保留执行顺序,这就是为什么您会看到所有内容都乱序.

TPL Dataflow blocks preserve the order of the input when they output. They don't preserve the order of execution while inside the block, that's why you see everything out of order.

您可能想要做的是将 ActionBlock 替换为 TransformBlock 以实际同时完成艰苦的工作并将其与 ActionBlock 链接一次更新一个 UI.

What you probably want to do is replace the ActionBlock with a TransformBlock to actually do the hard work concurrently and link it with an ActionBlock that updates the UI one at a time.

你也可以让这个块在 UI 线程上运行,这样你就不需要使用 Invoke:

You can also have this block run on the UI thread so you wouldn't need to use 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 } );

这篇关于从 ActionBlock 更新 UI 控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆