Matan Givoni
Matan Givoni

Reputation: 1126

Simple task without freezing the gui

I came across with a little problem. I have two threads one that executes a loop that needs to return/send a number to the GUI's thread every time. For this I use the BackGroundWorker and the ReportProgress .

Let say something like that:

I have a BackGroundWorker that executes (DoWork) a simple loop that counts from 0 to whatever. Every entry to the loop I send the counter using the ReportProgress event to the GUI's thread that will print the counter's value.

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        int count = 0;
        BackgroundWorker Worker = (BackgroundWorker)sender;

        while (count < 10000000)
        {
            Worker.ReportProgress(count);
            count++;
        }
    }

    void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        txt.Text = e.ProgressPercentage.ToString();
    }

Now, this opretion freezes the GUI.

I understand that the ReportProgress is invoking the ProgressChange handler at the thread that created the BackGroundWorker , So I think the loop is executeing so fast so the GUI's thread isn't succeeding to print the values as required.

What Can I do to perform a task like that without freezing the GUI?

I heard of the Dispatcher but I don't really sure for what it uses for.

Upvotes: 0

Views: 1193

Answers (3)

Ondra
Ondra

Reputation: 1647

The problem is that you are calling reportProgress every time something changes. You should call it only when you "need" to report progress. See MSDN http://msdn.microsoft.com/en-us/library/ka89zff4.aspx. Change your dowork to something like this:

 while (count < 10000000)
    {
        if ((count % 1000) == 0)
            Worker.ReportProgress(count);
        count++;
    }

This will call ReportProgress after each 1000 processed items and therefore not put unnecessary load to your GUI thread

Upvotes: 3

Martin James
Martin James

Reputation: 24907

You example code is attempting to update the GUI at a much faster rate than the GUI can process the update notification messages, so flooding the GUI Windows message queue with gunge and preventing it handling other messages - GUI freeze.

Monitoring the progress of high-rate operations in a non-GUI thread is one of the few times when polling is the better solution. Use a Forms.Timer event to read and display a 'currentProgress' value, perhaps returned by a method of the thread. 500ms is a reasonable timer value - human users cannot keep up with a changing integer value in an edit/text box at a rate much faster than that.

'Ideally', the read/write of the currentProgress value should be locked up, perhaps with an atomic op, but if you're only reading an int every 500ms, you probably don't even need that if the 'real' functionality of the thread means that the progress count is very unlikely to be continuously cached in a register.

Upvotes: 1

Daneo
Daneo

Reputation: 518

What Can I do to perform a task like that without freezing the GUID? :

Using dispatcher made me assume you're using WPF, anyways, it would be :

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    int count = 0;
    BackgroundWorker Worker = (BackgroundWorker)sender;

    while (count < 10000000)
    {
        Worker.ReportProgress(count);
        count++;
    }
}

void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    Dispatcher.BeginInvoke((Action)(() => { txt.Text = e.ProgressPercentage.ToString(); }));
}

Calling the Dispatcher.BeginInvoke causes the given Action to actually be executed on the UI thread, making sure no Exception is thrown for the cause of a thread other than the UI thread accessing a UI element.

Also, you might try this, just as an alternative by using a task.

Upvotes: 0

Related Questions