Как заставить задачу НЕ выполняться в потоке пользовательского интерфейса - PullRequest
11 голосов
/ 17 марта 2012

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

    void Do()
    {
        Debug.Assert(this.Dispatcher.CheckAccess() == true);
        Task.Factory.StartNew(ShortUIWork, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
    }

    void ShortUIWork()
    {
        Debug.Assert(this.Dispatcher.CheckAccess() == true);
        Task.Factory.StartNew(LongWork, TaskCreationOptions.LongRunning);
    }

    void LongWork()
    {
        Debug.Assert(this.Dispatcher.CheckAccess() == false);
        Thread.Sleep(1000);
    }

Так что Do () обычно вызывается из контекста интерфейса.И так же ShortUIWork, как определено TaskScheduler.Однако LongWork также вызывается в потоке пользовательского интерфейса, который, конечно, блокирует пользовательский интерфейс.

Как обеспечить, чтобы задача не выполнялась в потоке пользовательского интерфейса?

1 Ответ

9 голосов
/ 17 марта 2012

LongRunning - это просто намек на TaskScheduler.В случае SynchronizationContextTaskScheduler (как возвращено TaskScheduler.FromCurrentSynchronizationContext()) он, очевидно, игнорирует подсказку.

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

LongRunning - Указывает, что задача будет длительной, крупнозернистой операцией.Он дает подсказку TaskScheduler, что превышение подписки может быть оправдано.

Поскольку поток пользовательского интерфейса не является потоком пула потоков, не может возникнуть «избыточная подписка» (истощение пула потоков), такощущение, что подсказка не будет иметь никакого эффекта для SynchronizationContextTaskScheduler.

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

void ShortUIWork()
{
    Debug.Assert(this.Dispatcher.CheckAccess() == true);
    Task.Factory.StartNew(LongWork, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
...