Waiting for a long process and still updating UI

13,960

Solution 1

C# uses an event model -- what you want to do is dispatch the process that does the work and then have that process fire a custom event when done or use one of the threading events. While the process is running in the "background" release control back to the system from your code.

Solution 2

Firstly, why do you wish to avoid DoEvents()?

Secondly, you're using conflicting terms.

waiting == blocking

You say you don't want to block the UI thread, but you do want to wait for the task to finish. These are mutually exclusive states. If you're waiting for something to finish, you're blocking your thread.

If you want the UI to actually be usable (not blocked), then you don't wait for your task to finish. Just register an event handler to fire when it finishes. For instance with a BackgroundWorker, handle the RunWorkerCompleted event. For a Task, you can use a continuation to dispatch a callback to your main thread.

But it seems you just want the UI to be updated, not usable. Usually that only makes sense if you want a progress bar or some other UI animation to keep moving. In that case, I would open a modal dialog, kick off my task, and then wait on it while, yes, calling DoEvents().

    var dialog = new MessageBoxFormWithNoButtons("Please wait while I flip the jiggamawizzer");
    dialog.Shown += (_, __) =>
        {
            var task = Task.Factory.StartNew(() => WriteToDatabase(), TaskCreationOptions.LongRunning);
            while (!task.Wait(50))  // wait for 50 milliseconds (make shorter for smoother UI animation)
                Application.DoEvents(); // allow UI to look alive
            dialog.Close();
        }

    dialog.ShowDialog();

The modal dialog prevents the user from doing anything, but any animation will still work because of DoEvents() being called 20 times a second (or more).

(You would probably want to add special handling for different task completion states, but that's off-topic.)

Share:
13,960
Trevor Watson
Author by

Trevor Watson

Developer, Manager, Database Admin, hardware guy, planner. Jack of all trades, master of... some.

Updated on June 27, 2022

Comments

  • Trevor Watson
    Trevor Watson almost 2 years

    I've been attempting to create a task that writes to a database without blocking the UI thread. The biggest problem I'm having is waiting for that process to finish without the blocking happening.

    I've been trying to avoid using DoEvents (though it's used quite frequently through this program right now, I'd like to move out of using it while moving forward).

    I've attempted to create the process to run on a 2nd thread and waiting for it to finish as well as using a BackgroundWorker.

    The problem I have with this is not having the code run in a different thread, but trying to find a way to wait for it to finish.

    Basically, Right now I do the following:

    1. Connect to the database
    2. Create a background worker (or thread) to do the writing to the database (I will probably end up with the BackgroundWorker so I can use the ReportProgress
    3. Start the thread or BackgroundWorker
    4. Use a While loop to wait for the thread / BackgroundWorker to finish. For the thread, I wait for IsAlive to become false, for the BackgroundWorker, I toggle a boolean variable.
    5. I let the user know the process is finished.

    The problem is in #4.

    Doing a while loop with no code in it, or a Thread.Sleep(0) leaves the UI blocked (Thread.Sleep(0) makes the program take 100% of the program resources as well)

    So I do:

    while (!thread.IsAlive)
       Thread.Sleep(1);
    

    -or-

    while (bProcessIsRunning)
       Thread.Sleep(1);
    

    which blocks the UI.

    If I call Application.DoEvents() there, the UI is updated (though it's clickable, so I have to disable the entire form while this process runs).

    If I run the process synchronously, I still need to create some sort of way for the UI to be updated (in my mind, a DoEvents call) so it doesn't appear locked up.

    What am I doing wrong?