开发者

How to solve threading sync issues with unknown number of events in .NET?

开发者 https://www.devze.com 2023-03-12 09:28 出处:网络
We\'re using WebClient DownloadDataCompleted & DownloadProgressChanged events. We noticed that the progress event can fire an indeterminate number of times and not return from each callback, and s

We're using WebClient DownloadDataCompleted & DownloadProgressChanged events. We noticed that the progress event can fire an indeterminate number of times and not return from each callback, and still the DataCompleted event will fire. The reason DownloadProgressEvent doesn't return is because it's updating the ProgressBar on the form, which is going through a Control.Invoke cycle. We don't use BeginInvoke for other reasons (Progres开发者_JAVA百科sBar max and min change constantly and this cause assertions, since we can't sync up progress updates with progress bar max/min settings).

The question is: Whats the best approach for this?

Simply put, we don't want to acknowledge a completed download until the ProgressBar finishes updating. This implies something of a reverse semaphore that counts up, and is set when it goes back down to zero. We could just use a counter to increment/decrement when entering/leaving DownloadProgress callback, but I would have thought there's something more OS specific.


I would go for the following:

DownloadDataCompleted += delegate {
    progressBar.Invoke(() => {
        progressBar.Value = progressBar.Maximum;
        progressFinished = true;
    }
};

DownloadProgressChanged += delegate {
    progressBar.Invoke(() => {
        if (!progressFinished)
            progressBar.Value =
                  progressBar.Minimum +
                  (progressBar.Maximum - progressBar.Minimum) * progressRatio;
    }
};

There should be no problems with changing the Maximum and Minimum, since all the changes occur anyway in the UI thread.

(The code which changes Maximum or Minimum must take care about recalculating the Value, of course.)

P.S.: Edited the post, taking @Ben Voigt's suggestion into account.


I would be inclined to use BeginInvoke to handle the control update, but arrange for the an atomic update of fields holding the requested "current" and "maximum" values, along with a flag that indicates an update has been requested. The UI's update routine should not hold any locks while performing the update. If locks are used, the UI should grab the lock, make a local copy of the values (including the flag), clear the flag, release the lock, and redraw if necessary. The routine (on the other thread) which wants to request the update should grab the lock, update the values, and--if the flag isn't already set--set the flag and perform a BeginInvoke.

That will ensure that no matter how often the non-UI thread requests an update, at most one BeginInvoke will be outstanding. Further, every update will have usable values for "current" and "maximum".

0

精彩评论

暂无评论...
验证码 换一张
取 消