简体   繁体   中英

Understanding BeginInvoke in C#

I am trying to implement a "long wait" dialog to show a progress bar in a window during a long operation. I know there are some stock examples of this online. I am trying to understand why mine does not work so I can learn something about threading from it.

What happens when I run this is that the breakpoint at line 1 of process.Begin is never hit. Why is that?

public partial class LongWaitDlg : Window
{
    private ILongWaitProcess process;

    public LongWaitDlg(ILongWaitProcess process)
    {
        InitializeComponent();

        Progress.Maximum = 1;

        Progress.Value = process.IsLiveUpdating() ? 0 : 1;

        this.process = process;

        Dispatcher.BeginInvoke((Action) (() => this.ManageProgress()));
    }

    public void ManageProgress()
    {

        Dispatcher.BeginInvoke((Action)(() => process.Begin()));

        while (!Dispatcher.Invoke<bool>(process.IsComplete))
        {
            double progress = Dispatcher.Invoke<double>(process.GetProgress);
            Progress.Value = progress;
            Console.WriteLine(progress);
            Thread.Sleep(1000);
        }

        this.Close();
    }
}

You need to run your operation in another thread. Dispatcher.BeginInvoke allows you to continue execution on the primary thread, but it shouldn't be used to start a long-running unit of uninterrupted work because doing that will block all other activities on the primary thread, including other calls to Dispatcher.BeginInvoke , and responses required to keep your UI up to date.

Annotating your source, this is what is happening:

public void ManageProgress()
{
    // This queues up a message to run process.Begin(), 
    // but will not begin until this function and other callers ahead 
    // of it have returned control to the Dispatcher
    Dispatcher.BeginInvoke((Action)(() => process.Begin()));

    // This is waiting on a response from process, but it will get 
    // stuck in the loop here. Also, using Dispatcher.Invoke<> is suspect here.
    // By looping here, we prevent the prior call to BeginInvoke from working.
    while (!Dispatcher.Invoke<bool>(process.IsComplete))
    {
        // ...
    }

    this.Close();
}

Instead of using Dispatcher.BeginInvoke to launch your unit of work, consider using any number of alternatives, such as System.Task (async/wait in .net 4.5), System.Threading.Thread , or ThreadPool.QueueUserWorkItem . All of these will allow you to schedule work outside of the primary thread, and you can use Dispatcher.BeginInvoke from outside (your work item) to schedule updates to your UI, presumably to an updated version of your ManageProcess() method.

Asynchronous Programming with Async and Await at MSDN
Thread Class (System.Threading) at MSDN
ThreadPool.QueueUserWorkItem at MSDN

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM