Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
471 views
in Technique[技术] by (71.8m points)

c# - Update progress bar from async method

I am trying to understand better how can I update a windows forms progress bar from an async operation but I am getting some unexpected behavior from that.

Basically I am having a button which should after is being clicked to update a progress bar and then set it back to 0 once the progress bar gets 100% updated.

This is my code:

    private async void button1_Click(object sender, EventArgs e)
    {
        await CallMethodAsync().ContinueWith((prevTask) => 
        {
            prevTask.Wait();
            progressBar1.Invoke(new Action(() => { progressBar1.Value = 0; }));
        });
    }

    private static async Task ExecuteMethodAsync(IProgress<double> progress = null)
    {
        double percentComplete = 0;
        bool done = false;

        while (!done)
        {
            if (progress != null)
            {
                progress.Report(percentComplete);
            }

            percentComplete += 10;

            if(percentComplete == 100)
            {
                done = true;
            }
        }
    }

    private async Task CallMethodAsync()
    {
        var progress = new Progress<double>();
        progress.ProgressChanged += (sender, args) => { progressBar1.Increment(10); };
        await ExecuteMethodAsync(progress);
    }

Having this implementation the progress bar is not being updated at all even if I call "Wait()" on the operation that should update the value of the progress bar.

If i remove this part of code:

progressBar1.Invoke(new Action(() => { progressBar1.Value = 0; }));

the progress bar gets updated but it remains all the time like that, and I want to set it back to 0 once it was entirely filled so that I can update it again when I click again the button.

Could someone please explain me what am I doing wrong ?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

One of the reasons async-await syntax was invented because it was difficult to follow the sequence of instructions when tasks were concatenated using functions like ContinueWith.

If you use async-await it is seldom necessary to use statements like ContinueWith. After an await, the thread already continues with the statements after the await.

If the button is clicked, you want to call ExcecuteMethodAsync. This function takes an IProgress, because it wants to report progress regularly. You want to call this function asynchronously, so whenever the function has to wait for something, it doesn't really wait, but returns control to you so you could do other things instead of really waiting, until you encounter an await, in which case your caller continues processing until he encounters an await, etc.

The nice thing with async-await is that the thread that continues after your call to an async function has the same context as the calling thread. This means that you can regard it as your original thread. No InvokeRequired, no need to protect data with mutexes etc.

Your function could be simplified as follows:

async Task CallMethodAsync()
{
    var progress = new Progress<double>();
    progress.ProgressChanged += OnProgressReported;

    await ExecuteMethodAsync(progress);
}

private void OnProgressReported(object sender, ...)
{
    // because this thread has the context of the main thread no InvokeRequired!
    this.progressBar1.Increment(...);
}

private async void button1_Click(object sender, EventArgs e)
{
    await CallMethodAsync();
}

So when the button is clicked, CallMethodAsync is called. This function will create A Progress object and subscribes on its Report event. Note that this is still your UI-thread. Then it calls ExecuteMethodAsync, which will regularly raise event Report, which is handled by OnProgressReported.

Because ExecuteMethodAsync is async, you can be sure there is somewhere an await in it. This means that whenever it has to await, control returns to the caller, which is CallMethodAsync, until is encounters an await, which in this case is immediately.

Control goes up the call stack to the caller, which is button1_click, where it immediately encounters an await, so control goes up the call stack, etc.

All these controls have the same context: it is as if they are the same thread.

An article that helped me a lot to understand async-await is this interview with Eric Lippert. Search somewhere in the middle for async await

Another articel that helped me a lot to learn good practices were this article by the ever so helpful Stephen Cleary and Async/Await - Best Practices in Asynchronous Programming also by Stephen Cleary


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...