Ожидать задание в течение х времени и сообщать о прогрессе - PullRequest
8 голосов
/ 06 ноября 2011

Мне нужен лучший дизайн для спящего Task, чем Thread.Sleep, я использую класс Task.

В моем wpf-приложении я запускаю задачу, эта задача выполняет еще пару задач, каждая из которых сначала входит в систему и получает доступ к веб-сайту в Интернете, после входа в систему им нужно подождать пару секунд и сообщить пользователю, когда они продолжат работу. и это моя проблема, потому что с Thread.Sleep я не могу сообщить о прогрессе.

Есть больше функций, чем вход в систему, это около 5-6, все они запрашивают интернет-ресурс, и между ними должно быть время ожидания с отчетом об оставшемся времени, отправляемом до графического интерфейса, эта 5-6 функция все в одна задача, но может быть много задач.

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

У вас есть идеи для этой проблемы, как ее улучшить, может быть, у вас уже есть какие-то шаблоны проектирования для такой проблемы?

Я также слышал, что использование Thread.Sleep - плохая практика проектирования.

РЕДАКТИРОВАТЬ: Никто не знает? Что с каким-то автономным таймером с ожиданием потока, как waithandle, autoresetevent, кто-нибудь?

Ответы [ 4 ]

6 голосов
/ 09 ноября 2011

Решение состоит в том, чтобы рассматривать его как асинхронный обратный вызов, а не синхронное ожидание. Если вы используете Async CTP, правильным способом будет:

async item => { await Task.Delay(1000); Process(item); }

Это также кажется идеальным вариантом использования DataFlow или Rx.

Использование реактивных расширений:

static void Track(int timeout, int frequency, string item)
    {
        Observable.Interval(TimeSpan.FromSeconds(frequency)) //produces 0, 1, 2.. with the interval
                  .Do(i => Console.WriteLine("Working on {0}", item)) // work on item
                  .TakeUntil(Observable.Timer(TimeSpan.FromSeconds(timeout))) //stop once the timer publishes a value
                  .Subscribe
                  (
                        i => Console.WriteLine("Reporting {0}%", ((double)(i + 1) / timeout * 100)), // the interval reaches OnNext
                        e => Console.WriteLine("Error!"), // error occured 
                        () => Console.WriteLine("Completed") // observable completed
                  );
    }

При вызове этого с Track(timeout: 5, frequency: 1, item: "http://example.com/?") получается:

Working on http://example.com/?
Reporting 20%
Working on http://example.com/?
Reporting 40%
Working on http://example.com/?
Reporting 60%
Working on http://example.com/?
Reporting 80%
Completed
1 голос
/ 15 ноября 2011

Забудьте об использовании Thread.Sleep. Вместо этого запустите вашу задачу в фоновом потоке и используйте WaitHandle с AutoResetEvent. (Ссылки: WaitHandle / WaitOne / Автосброс )

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

1) Ваша ветка сообщает, что выполнила свою задачу с помощью yourwaithandle.Set ();

или

2) тайм-ауты ожидающего потока (значение тайм-аута устанавливается в качестве параметра для метода WaitOne ()).

1 голос
/ 07 ноября 2011

Вместо того чтобы выполнять одну из этих полдюжины функций (вход в систему и т. Д.), А выполнять одну задачу, сделайте каждую функцию отдельной задачей. Затем вы можете создать приоритетную очередь, упорядоченную по времени. Когда вы хотите запустить одну из этих мета-задач, вы помещаете ее первую функцию в очередь вместе с данными о состоянии (то есть url, идентификатор пользователя, пароль и т. Д.).

Когда каждая функция завершена, она ставит в очередь следующую функцию, которая должна быть выполнена. Поэтому, если первые две функции - это login и get_data, у вас будет:

queue login with state data to execute at DateTime.Now
When login is finished, it queues get_data with the state data and DateTime.Now + 5 seconds (or whatever time)

Когда выполняется последняя функция, она отправляет результаты куда-нибудь.

Затем можно настроить таймер, который будет опрашивать очередь 10 раз в секунду (или обновлять время следующего такта таймера при каждом добавлении чего-либо в очередь). Он может запускать отдельные задачи по мере необходимости.

Новый Async CTP может уже иметь такую ​​вещь: «выполнить задачу во время». Может быть стоит посмотреть на это.

Что касается отчета о прогрессе, каждая функция может сообщать о прогрессе при запуске (т. Е. "Войти в систему" ... "ждать 5 секунд, чтобы получить данные" ... "получать данные" и т. Д.) Если вы хотите, вы можете заставить поток таймера периодически обходить очередь приоритетов и сообщать, когда будут выполнены конкретные задачи. Хотя это может быть излишним.

И то, что вы слышали правильно: Thread.Sleep - это исключительно плохая идея, особенно когда вы работаете с потоками пула потоков.

0 голосов
/ 09 ноября 2011

Рассматривали ли вы использование таймера (из System.Timers) для выполнения промежуточных обновлений сообщений «оставшегося времени»? Каждая из ваших задач может установить ожидаемое время для завершения, тогда ваша задача таймера может быть ответственна за обратный отсчет и обновление пользовательского интерфейса по мере необходимости. Thread.Sleep не требуется, и Timer выполнит свой код Tick в потоке пула потоков.

...