Для чего нужен Task.RunSynchronously? - PullRequest
0 голосов
/ 11 октября 2018

Мне просто интересно, для чего этот метод?В каком сценарии я могу использовать этот метод.

Моя первоначальная мысль: RunSynchronously - для вызова асинхронного метода и его синхронного запуска без возникновения проблемы взаимоблокировки, как это делает .wait().

Однако, согласно MSDN ,

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

Зачем нужнаTaskScheduler здесь, если задача собирается работать в вызывающем потоке?

Ответы [ 2 ]

0 голосов
/ 11 октября 2018

Сначала давайте посмотрим на этот код:

public async static Task<int> MyAsyncMethod()
{
   await Task.Delay(100);
   return 100;
}

//Task.Delay(5000).RunSynchronously();                        // bang
//Task.Run(() => Thread.Sleep(5000)).RunSynchronously();     // bang
// MyAsyncMethod().RunSynchronously();                      // bang

var t = new Task(() => Thread.Sleep(5000));
t.RunSynchronously();                                     // works

В этом примере мы попытались вызвать RunSynchronously для задачи, которая:

  • Возвращает другую задачу срезультат (задача обещания)
  • является «горячей» задачей
  • Другая задача обещания, созданная async await
  • «холодной» задачей с делегатом

Какие статусы будут у них после создания?

  • Ожидание активации
  • WaitingToRun
  • WaitingForActivation
  • Создано

Все «горячие» задачи создаются со статусом WaitingForActivation или WaitingToRun и связаны с планировщиком задач.

Метод RunSynchronously знает, как работать только с «холодными» задачами, содержащими делегат икоторые имеют статус Created.

Заключение:

Метод RunSynchronously, вероятно, появился, когда не было «горячих» задач или они не использовались широко и были созданы дляопределенной цели.

Мы можем захотеть использовать его в случае, когда нам нужна «холодная» задача с кастомами TaskScheduler, в противном случае он устарел и бесполезен.

Для синхронного запуска «горячей» задачи (которую мы должны избегать) мы могли бы использовать task.GetAwaiter().GetResult().В качестве бонуса он вернет оригинальное исключение, а не экземпляр AggregateException.

0 голосов
/ 11 октября 2018

RunSynchronously делегирует решение о том, когда начинать задачу, текущему планировщику задач (или переданному в качестве аргумента).

Я не уверен, почему он существует (возможно, для внутреннего или устаревшего использования).), но трудно представить себе полезный вариант использования в текущих версиях .NET . @ Фабьян имеет возможное объяснение в своем комментарии к вопросу.

RunSynchronously просит планировщика запустить его синхронно, но тогда планировщик вполне может проигнорировать подсказку и запустить его впоток пула потоков, и ваш текущий поток будет синхронно блокироваться, пока он не будет завершен.

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

RunSynchronously также вызовет исключение, если задача уже запущена или завершена / неисправна (это означает, что вы не сможете использовать ее в асинхронных методах).

Этот код может прояснить другое поведение:

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

class Scheduler : TaskScheduler
{
    protected override void QueueTask(Task task) => 
        Console.WriteLine("QueueTask");

    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
    {
        Console.WriteLine("TryExecuteTaskInline");

        return false;
    }

    protected override IEnumerable<Task> GetScheduledTasks() => throw new NotImplementedException();
}

static class Program
{
    static void Main()
    {
        var taskToStart = new Task(() => { });
        var taskToRunSynchronously = new Task(() => { });

        taskToStart.Start(new Scheduler());
        taskToRunSynchronously.RunSynchronously(new Scheduler());
    }
}

IЕсли вы попытаетесь прокомментировать Start или RunSynchronously и запустите код, вы увидите, что Start попытается поставить задачу в очередь в планировщике, а RunSynchronously попытается выполнить ее в строке и, если произойдет сбой (вернет false), будетпросто поставь в очередь.

...