Update GUI from a non-GUI-Thread -> Not working with BeginInvoke()

六月ゝ 毕业季﹏ 提交于 2019-12-13 04:43:30

问题


I have created a simple WindowsForms-Application with one button and one label. If I click the button then the label should display the numbers from 1 to 100000.

But if I click the button then the GUI freezes till the program counted to 100000 and then the label displays 100000 and the GUI stops freezing.

The counting from 1 to 100000 is executed in a new thread (not the GUI thread) and then changing of the labeltext I try with BeginInvoke, but it don't works...

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;
                    }
                    ));
            }

        }

    }
}

回答1:


BeginInvoke is asyncrhonous. This means that it queues the action to take place in the UI thread and then continues on with its business. Queueing up 100,000 actions in the UI thread doesn't actually take long at all. It in fact takes quite a lot less time than actually executing each of those actions. This means that the queue ends up being flooded with all of these requests to update the text. When a new request is added, say, a request to repaint the form to display the new value, or respond to a mouse click event on the form, it is placed at the end of the queue, after those many thousands of other requests.

If you use Invoke, instead of BeginInvoke, then the background worker won't move on to queue the next item until the previous item has finished running in the UI thread, keeping it from getting ahead of the UI thread and flooding the queue. This means that any other UI events, such as the events to actually repaint the screen, don't have more than at most one item in the queue to wait behind.




回答2:


For every value a thread context switch should be done. If you do this 100000 times, the UI Thread is freezing. This is caused by the asynchronouse calls. The UI thread is nothing else doing than EXECUTE this delegates. There is no time for the UI to get work items from the MessagePump unitil the last delegate is invoked. The Update of the control is triggered via WindowMessages by the MessagePump.

Now the value of the last delgate is set to the control.




回答3:


Here's a naive ReactiveUI way:

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());
    }
}

depends on the current reactiveui-winforms package



来源:https://stackoverflow.com/questions/22459486/update-gui-from-a-non-gui-thread-not-working-with-begininvoke

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!