Как решить проблемы синхронизации потоков с неизвестным количеством событий в .NET? - PullRequest
2 голосов
/ 13 июня 2011

Мы используем события WebClient DownloadDataCompleted & DownloadProgressChanged. Мы заметили, что событие progress может вызывать неопределенное количество раз и не возвращаться из каждого обратного вызова, и все же событие DataCompleted будет запускаться. Причина, по которой DownloadProgressEvent не возвращается, заключается в том, что он обновляет ProgressBar в форме, которая проходит цикл Control.Invoke. Мы не используем BeginInvoke по другим причинам (ProgressBar макс. И мин. Постоянно изменяются, и это вызывает утверждения, поскольку мы не можем синхронизировать обновления прогресса с настройками индикатора выполнения / макс. / Мин.)

Вопрос: каков наилучший подход для этого?

Проще говоря, мы не хотим подтверждать завершенную загрузку, пока ProgressBar не закончит обновление. Это подразумевает что-то вроде обратного семафора, который считает, и устанавливается, когда он возвращается к нулю. Мы могли бы просто использовать счетчик для увеличения / уменьшения при входе / выходе обратного вызова DownloadProgress, но я бы подумал, что есть что-то более специфичное для ОС.

Ответы [ 2 ]

2 голосов
/ 13 июня 2011

Я бы пошел на следующее:

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;
    }
};

Не должно быть проблем с изменением максимума и минимума, поскольку все изменения происходят в любом случае в потоке пользовательского интерфейса.

(Конечно, код, который меняется Maximum или Minimum, должен позаботиться о пересчете Value.)

P.S .: Отредактировал пост, учитывая предложение @Ben Voigt.

0 голосов
/ 14 июня 2011

Я был бы склонен использовать BeginInvoke для обработки обновления элемента управления, но организовал атомарное обновление полей, содержащих запрошенные «текущие» и «максимальные» значения, а также флаг, указывающий, что было запрошено обновление. Процедура обновления пользовательского интерфейса не должна удерживать никаких блокировок при выполнении обновления. Если используются блокировки, пользовательский интерфейс должен захватить блокировку, сделать локальную копию значений (включая флаг), снять флаг, снять блокировку и перерисовать при необходимости. Подпрограмма (в другом потоке), которая хочет запросить обновление, должна захватить блокировку, обновить значения и - если флаг еще не установлен - установить флаг и выполнить BeginInvoke.

Это гарантирует, что независимо от того, как часто поток, не являющийся пользовательским интерфейсом, запрашивает обновление, самое большее один BeginInvoke будет выдающимся. Кроме того, каждое обновление будет иметь полезные значения для «текущего» и «максимального».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...