Ожидание созданной вручную задачи останавливает приложение ASP. NET - PullRequest
1 голос
/ 13 февраля 2020

Я сделал очередь, которая содержит задачи для выполнения. После создания некоторых задач вручную с помощью метода new Task() в Returns все мое приложение зависает - await current;. Тело задачи даже не сработало.

ConfigureAwait(false) не помогло.

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

Скриншот кода отладчика IDE

--- ОБНОВЛЕНИЕ ---

Код работает, когда я использую код ниже. С await это не так. Есть идеи?

current.Start();
current.Wait();

Оригинальный код

private readonly Queue<Task> _pipe;

public IPipeBuilder<TOutput> Returns(Func<IEnumerable<IExecutionResult>, TOutput> outputBuilder)
{
   _pipe.Enqueue(new Task(() => // this task causes a problem and breakpoint isn't hit inside 
   {
      _output = outputBuilder(_results);
   }));

   return this;
}

public async Task<TOutput> Execute()
{
   Task current;

   while (_pipe.TryDequeue(out current))
   {
      if (current.IsCommandExecution())
      {
         IExecutionResult result = await (Task<IExecutionResult>)current; // this awaits successfully
         _results.Add(result);
      }
      else
      {
         await current; // hangs here
      }
   }

   return await Task.FromResult(_output);
}

Использование

[HttpGet("eventflow/pipe/issue/add/{title}")]
public async Task<IActionResult> PipeAction(string title)
   => Ok(
      await Pipe<IExecutionResult>()
           .Validate(title)
           .Handle<AddIssueCommand>(IssueId.New, title)
           .Returns(results => results.First())
           .Execute());

Ответы [ 2 ]

1 голос
/ 13 февраля 2020

Никогда не используйте конструктор Task . Это удваивается на ASP. NET, поскольку построенные задачи всегда являются делегированными задачами, которые мешают использованию ASP. NET пула потоков . Фактическая причина, по которой await зависает, заключается в том, что задачи, созданные вручную, должны быть запущены .

Если у вас есть синхронная работа, которую нужно заключить в Task, чтобы работать вместе асинхронных задач, тогда вы должны использовать Task.CompletedTask и Task.FromException:

private static Task SynchronousWork(Func<IEnumerable<IExecutionResult>, TOutput> outputBuilder)
{
  try { _output = outputBuilder(_results); return Task.CompletedTask; }
  catch (Exception ex) { return Task.FromException(ex); }
}

public IPipeBuilder<TOutput> Returns(Func<IEnumerable<IExecutionResult>, TOutput> outputBuilder)
{
  _pipe.Enqueue(SynchronousWork(outputBuilder));
  return this;
}

Однако обратите внимание, что это выполняет outputBuilder немедленно , что может быть нежелательно из-за его стороны эффекты на _results и _output. Если вы хотите очередь отложенного выполнения , то тип в очереди должен быть изменен с Task на Func<Task>. Затем вы можете добавить к нему следующее:

public IPipeBuilder<TOutput> Returns(Func<IEnumerable<IExecutionResult>, TOutput> outputBuilder)
{
  _pipe.Enqueue(() =>
  {
    try { _output = outputBuilder(_results); return Task.CompletedTask; }
    catch (Exception ex) { return Task.FromException(ex); }
  });

  return this;
}

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

public async Task<TOutput> Execute()
{
  while (_pipe.TryDequeue(out var currentFunc))
  {
    var currentTask = currentFunc();
    if (currentTask.IsCommandExecution())
    {
      IExecutionResult result = await (Task<IExecutionResult>)currentTask;
      _results.Add(result);
    }
    else
    {
      await currentTask;
    }
  }

  return _output;
}
0 голосов
/ 14 февраля 2020

Хорошо, спасибо. Я закончил с таким классом и Queue<Func<Task>>, как вы сказали.

public sealed class SyncTaskWrapper
{
    private Func<Task> _action;

    public SyncTaskWrapper(Action action)
        => _action = CreateFunc(action);

    private static Func<Task> CreateFunc(Action action)
        => () =>
        {
            try
            {
                action();

                return Task.CompletedTask;
            }
            catch (Exception exception)
            {
                return Task.FromException(exception);
            }
        };

    public static implicit operator Func<Task>(SyncTaskWrapper @this)
        => @this._action;
}

с использованием

_pipe.Enqueue(new SyncTaskWrapper(() =>
    _output = outputBuilder(_results)));
...