从非GUI线程更新GUI - >不使用BeginInvoke()

时间:2014-03-17 16:10:55

标签: c# multithreading user-interface begininvoke

我创建了一个简单的WindowsForms-Application,其中包含一个button和一个label。 如果单击按钮,则标签应显示1到100000之间的数字。

但是,如果我点击button,那么GUI会冻结,直到程序计数到100000,然后标签显示100000,GUI停止冻结。

从1到100000的计数在一个新线程(不是GUI线程)中执行,然后用labeltext更改BeginInvoke,但它不起作用..

using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e) {

            new Thread(DoSomethingExpensive).Start();

        }

        void DoSomethingExpensive() {

            for (int i = 0; i < 100000; i++) {
                this.label1.BeginInvoke((Action)(() => {
                    label1.Text = "" + i;
                    }
                    ));
            }

        }

    }
}

3 个答案:

答案 0 :(得分:2)

BeginInvoke是异步的。这意味着它将操作排队到UI线程中,然后继续其业务。在UI线程中排队100,000个动作实际上并不需要很长时间。事实上,它比实际执行每个动作花费的时间要少得多。这意味着队列最终充满了所有这些请求以更新文本。当添加新请求时,例如,重新绘制表单以显示新值的请求,或者响应表单上的鼠标单击事件时,在数千个其他请求之后,它将被放置在队列的末尾。

如果您使用Invoke而不是BeginInvoke,则后台工作人员不会继续排队下一个项目,直到上一个项目在UI线程中运行完毕,保留它从领先UI线程并充斥队列。这意味着任何其他UI事件(例如实际重新绘制屏幕的事件)都不会超过队列中最多一个项目等待。

答案 1 :(得分:0)

对于每个值,应该进行线程上下文切换。如果你这样做100000次,UI线程就会冻结。这是由asynchronouse调用引起的。除了EXECUTE这个委托之外,UI线程没有其他功能。用户界面没有时间从MessagePump unitil获取工作项,调用最后一个委托。 MessagePump通过WindowMessages触发控件的更新。

现在最后一个delgate的值设置为控件。

答案 2 :(得分:0)

这是一种天真的ReactiveUI方式:

public partial class Form1 : Form
{
    IDisposable subscription;
    IObservable<long> sequence;

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        if (subscription != null)
            subscription.Dispose();

        sequence = Observable
            .Interval(TimeSpan.FromMilliseconds(1))
            .Take(10000); // generate a timed sequence

        subscription = sequence // act upon the sequence
            .ObserveOn(SynchronizationContext.Current)
            .Subscribe(x => label1.Text = x.ToString());
    }
}

取决于当前的reactiveui-winforms