.NET: механизм синхронизации длительных задач - PullRequest
7 голосов
/ 20 февраля 2010

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

  1. Отправить некоторую информацию о ходе выполнения задачи (прогресс, ведение журнала активности и т. Д.)
  2. Есть способ прервать задачу до ее завершения, если был установлен какой-либо внешний сигнал или свойство.

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

Мой вопрос: Есть ли уже встроенный механизм в .NET Framework (3.5 или ниже) для описанной выше проблемы?

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

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

ОБНОВЛЕНИЕ : ОК, я думаю, что мое описание проблемы нуждается в небольшом уточнении :). Когда я говорю «длительный», я не имею в виду «длинный» в смысле рабочего процесса. Я работаю над картографическим приложением WinForms, которое делает все что угодно, например, генерирует рельефные контуры . Для этого сначала необходимо загрузить файлы данных высот с FTP-сервера, разархивировать их, а затем выполнить некоторые вычисления. Я написал код для этого давным-давно, но для того, чтобы сделать его более удобным для графического интерфейса, я должен установить различные проверки, например, чтобы пользователь нажал кнопку «Прервать» и остановил процесс.

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

Ответы [ 4 ]

6 голосов
/ 20 февраля 2010

Это звучит очень похоже на Windows Workflow Foundation .

2 голосов
/ 20 февраля 2010

Взгляните на образец саги.Он не встроен в структуру, но может быть реализован.Альтернативно, и NServiceBus и MassTransit имеют реализации этого.У Арнона RGO есть черновик из его книги (будет ли он когда-нибудь закончен), описывающий его здесь .

Мой опыт работы с NServiceBus намного проще, чем WF, и также более мощный (хотя я не смотрел на WF 4, который по всем описаниям является почти полной переработкой WF, поскольку Microsoft признала недостатки этого).

Даже если вам не нужна инфраструктура, такая как NServiceBus или MassTransitНа сам шаблон стоит обратить внимание, так как он очень близко соответствует вашему проблемному пространству из того, что вы описали.

1 голос
/ 20 февраля 2010

Это зависит от того, насколько сложна ваша система. Для относительно простых задач вы, вероятно, можете использовать класс BackgroundWorker из .NET 2.0. Он поддерживает создание отчетов о ходе операции с использованием события OnProgressChanged, а также отмену фоновой задачи с использованием метода CancelAsync.

Класс управляется событиями, но, поскольку это уже часть класса, я не думаю, что это для вас накладные расходы:

var bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(RunWorkerCompleted);
bw.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);
  • Метод DoWork выполняется для запуска фоновой задачи (он может сообщить о ходе выполнения, вызвав bw.ReportProgress и проверив наличие ожидающих отмены, используя bw.CancellationPending).

  • Метод RunWorkerCompleted выполняется в потоке графического интерфейса пользователя после завершения операции (что дает хороший способ синхронизации, не беспокоясь о параллелизме)

  • Событие ProgressChanged запускается всякий раз, когда ваш метод DoWork сообщает о некотором прогрессе.

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

0 голосов
/ 22 февраля 2010

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

...