Как передать информацию из ThreadPool.QueueUserWorkItem обратно в поток пользовательского интерфейса? - PullRequest
4 голосов
/ 14 августа 2010

У меня довольно простой вопрос.

Я пишу простую утилиту, которая будет запускать различные сценарии SQL на основе параметров, определенных пользователем.

Чтобы пользовательский интерфейс реагировал и предоставлял информацию о состоянии выполняемых сценариев, я решил, что использование ThreadPool.QueueUserWorkItem будет уместно для выполнения различных сценариев (через SMO).

Однако меня немного смущает вопрос о том, как я могу ретранслировать выходную информацию, которую SMO вернет обратно в поток пользовательского интерфейса.

Для этой утилиты я использую WPF и MVVM для презентации. Я думаю, что у меня был бы класс ScriptWorker, в который я мог бы передать параметры, местоположения и порядок, в котором нужно запускать сценарии.

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

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

Я основываю свои предположения на этой статье Microsoft:

http://msdn.microsoft.com/en-us/library/3dasc8as(VS.80).aspx

Спасибо за информацию!

Ответы [ 2 ]

8 голосов
/ 14 августа 2010

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

Я рекомендую использовать новую функцию Task .NET 4.0. Он делает именно то, что вам нужно, включая синхронизацию результата или условий ошибки с другим потоком (в данном случае с потоком пользовательского интерфейса).

Если .NET 4.0 не вариант, то я бы порекомендовал либо BackgroundWorker (если ваша фоновая обработка не слишком сложна), либо асинхронные делегаты, такие как упомянутый Ганс. Если вы используете асинхронные делегаты, то используйте класс AsyncOperation для перенаправления результатов обратно в поток пользовательского интерфейса.

Опция Task очень хороша, потому что она обрабатывает родительские / дочерние задачи очень естественно. BackgroundWorker не может быть вложенным. Другое соображение - отмена; Task и BackgroundWorker имеют встроенную поддержку отмены, но для асинхронных делегатов вам придется делать это самостоятельно.

Единственное место, где Task немного сложнее, чем BackgroundWorker, - это отчеты о ходе выполнения. Это не так просто, как BackgroundWorker, но у меня есть обертка в моем блоге , чтобы минимизировать это.

Подводя итог, в порядке предпочтения:

  1. Task - поддерживает правильное маршалинг ошибок, концепцию результата, отмену и вложение родительских / дочерних элементов. Единственное его слабое место в том, что отчеты о ходе работы не просты (вам нужно создать еще одну задачу и запланировать ее для потока пользовательского интерфейса).
  2. BackgroundWorker - поддерживает правильное распределение ошибок, концепцию результата, отмену и отчеты о ходе выполнения. Его единственная слабость в том, что он не поддерживает вложенность родительский / дочерний, что ограничивает его использование в API, например, для бизнес-уровня.
  3. Delegate.BeginInvoke с AsyncOperation - поддерживает правильное распределение ошибок, концепцию результата и отчеты о прогрессе. Тем не менее, нет встроенной концепции отмены (хотя это можно сделать вручную, используя volatile bool). Он также не поддерживает вложенность родителей / детей.
  4. Delegate.BeginInvoke с SynchronizationContext - это то же самое, что и опция (3), за исключением того, что она использует SynchronizationContext напрямую. Код немного сложнее, но компромисс в том, что поддерживается вложение родительских / дочерних элементов. Все остальные ограничения идентичны опции (3).
  5. ThreadPool.QueueUserWorkItem с AsyncOperation или SynchronizationContext - поддерживает концепцию отчетов о проделанной работе. Отмена страдает той же проблемой, что и опция (3). Выделение ошибок не легко (в частности, сохранение трассировки стека). Кроме того, вложение родителей / потомков возможно только в том случае, если вместо AsyncOperation используется SynchronizationContext. Кроме того, этот параметр не поддерживает концепцию результата, поэтому любое возвращаемое значение (я) необходимо передавать в качестве аргументов.

Как видите, Task явный победитель. Его следует использовать, если только .NET 4.0 не является опцией.

0 голосов
/ 14 августа 2010

Эта статья содержит простой пример того, что вы хотите.

Чтобы вернуться к потоку пользовательского интерфейса, вам нужна ссылка на интерфейс ISynchronizeInvoke.Класс Form, например, реализует этот интерфейс.

В псевдокоде вы можете сделать что-то вроде этого:

public class MyForm : Form
{
    private OutputControl outputControl;

    public void btnClick(...)
    {
        // Start a long running process that gives feedback to UI.
        var process = new LongRunningProcess(this, outputControl);
        ThreadPool.QueueUserWorkItem(process.DoWork);
    }
}

class LongRunningProcess
{
    // Needs a reference to the interface that marshals calls back to the UI
    // thread and some control that needs updating.
    public LongRunningProcess(ISynchonizeInvoke invoker,
                              OutputControl outputControl)
    {
        this.invoker = invoker;
        this.outputControl = outputControl;
    }

    public void DoWork(object state)
    {
        // Do long-running job and report progress.
        invoker.Invoke(outputControl.Update(...));
    }
}

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

Подход, описанный выше, является довольно низкоуровневым, но дает вам большой контроль,особенно о том, как вы хотите сообщить о прогрессе.BackgroundWorker дает вам решение более высокого уровня, но с меньшим контролем.Вы можете предоставить состояние прогресса только через нетипизированное UserState свойство.

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