代码之家  ›  专栏  ›  技术社区  ›  Mist

如何从异步例程更新UI,而无需启动或调用[duplicate]

  •  -1
  • Mist  · 技术社区  · 7 年前

    我看到许多代码,他们使用BeginInvoke从另一个线程更新UI。是否可以从异步函数更新UI而无需启动InVoke?

    private async void button1_Click(object sender, EventArgs e)
            {
                button1.Enabled = false;
                var count = 0;
    
                await Task.Run(() =>
                {
                    for (var i = 0; i <= 500; i++)
                    {
                        count = i;
                        BeginInvoke((Action)(() =>
                        {
                            label1.Text = i.ToString();
    
                        }));
                       Thread.Sleep(100);
                    }
                });
    
                label1.Text = @"Counter " + count;
                button1.Enabled = true;
            }
    

    看到下面的代码,我已经从一个链接,显示没有BeginInvoke,我们可以更新用户界面时使用task.run。

    private readonly SynchronizationContext synchronizationContext;
    private DateTime previousTime = DateTime.Now;
    
    public Form1()
    {
        InitializeComponent();
        synchronizationContext = SynchronizationContext.Current;
    }
    
    private async void button1_Click(object sender, EventArgs e)
    {
        button1.Enabled = false;
        var count = 0;
    
        await Task.Run(() =>
        {
            for (var i = 0; i <= 5000000; i++)
            {
                UpdateUI(i);
                count = i;
            }
        });
    
        label1.Text = @"Counter " + count;
        button1.Enabled = true;
    }
    
    public void UpdateUI(int value)
    {
        var timeNow = DateTime.Now;
    
        if ((DateTime.Now - previousTime).Milliseconds <= 50) return;
    
        synchronizationContext.Post(new SendOrPostCallback(o =>
        {
            label1.Text = @"Counter " + (int)o;
        }), value);             
    
        previousTime = timeNow;
    } 
    

    告诉我 synchronizationContext和BeginInvoke 两者都一样吗?应该使用哪个线程更新UI?哪一个效率最高?

    请指导我,我是async/await&方面的新手;任务使用情况。

    3 回复  |  直到 7 年前
        1
  •  2
  •   Alexander Petrov    7 年前

    使用 Progress 班级。

    private async void button1_Click(object sender, EventArgs e)
    {
        button1.Enabled = false;
        var count = 0;
    
        // The Progress<T> constructor captures UI context,
        // so the lambda will be run on the UI thread.
        IProgress<int> progress = new Progress<int>(value =>
        {
            label1.Text = value.ToString();
        });
    
        await Task.Run(() =>
        {
            for (var i = 0; i <= 500; i++)
            {
                count = i;
                progress.Report(i);
                Thread.Sleep(100);
            }
        });
    
        label1.Text = @"Counter " + count;
        button1.Enabled = true;
    }
    
        2
  •  2
  •   Henk Holterman    7 年前

    要避免的是 Task.Run()

    private async void button1_Click(object sender, EventArgs e)
    {
        button1.Enabled = false;
        var count = 0;
    
        for (var i = 0; i <= 500; i++)
        {
            count = i;          
            label1.Text = i.ToString();
            await Task.Delay(100);         // do async I/O or Task.Run() here
        }    
    }
    
        3
  •  1
  •   Fabio    7 年前

    Invoke .

    private async Task UpdateDatabase()
    {
        using (var connection = new SqlConnection(connectionString))
        using (var command = connection.CreateCommand())
        {
            command.CommandText = "SELECT Id FROM Table";
            await connection.OpenAsync();
            using (var reader = await command.ExecuteReaderAsync())
            {
                var rowsCount = 0;
                // Since execution will happen on same thread 
                // you will be able update UI controls.
                Label1.Text = $"Rows: {rowsCount}";
    
                while (await reader.ReadAsync())
                {
                    rowsCount ++;
                    Label1.Text = $"Rows: {rowsCount}";
                }
            }
        }
    }
    
    private async void button1_Click(object sender, EventArgs e)
    {
        button1.Enabled = false;
    
        await UpdateDatabase();
    
        button1.Enabled = true;
    }
    

    对于希望使用“进度”信息更新UI的特定情况,可以使用 BackgroundWorker Progress