Как ждать асин c в C# ASP.NET - PullRequest
2 голосов
/ 30 марта 2020

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

Я попробовал следующий код:

[HttpPost]
[ActionName("addJob")]
public string AddJob([FromBody] Task task)
{
   // Add task ...

   RemoveTaskAfterTime(task);

   return "Job has been added";
}

private async Task RemoveTaskAfterTime(Task task)
{
   System.Diagnostics.Debug.WriteLine("started to wait");
   await Task.Delay(5000);
   System.Diagnostics.Debug.WriteLine("remove task");
}

По какой-то причине вызывается «начал ждать», но «удалить задачу» не. Он работает с Thread.sleep, но в этом случае ответ также занимает 45 минут, так что это не решение. Было бы здорово, если бы кто-нибудь мог мне помочь! Заранее спасибо

Ответы [ 3 ]

2 голосов
/ 30 марта 2020

Я полагаю, что проблема в Task.Delay, который использовался.

Task.Delay следует использовать в асинхронных c методах

45 минут слишком долго ждать в память (однако это возможно). Что бы вы делали с заданиями, ожидающими в памяти, если служба (пул приложений, сервер и т. Д.) Перезапускается?

Вы можете использовать базу данных, чтобы пометить задания как waiting, используя метод AddJob. Время начала работы должно быть установлено, чтобы проверить возраст работы позже.

Затем вы можете использовать BackgroundService , чтобы проверить возраст всех ожидающих работ. Вы можете делать эти проверки каждую минуту (например). Найти задания, которые ждут более 45 минут, и освободить их (установите статус задания на available)

2 голосов
/ 30 марта 2020

Ваша проблема относится к области.

Возможно, вы не задумывались об этом, но AddJob - это метод экземпляра, определенный в классе. IIS обрабатывает HTTP-запрос, создавая экземпляр объекта и вызывая метод. Дочерний поток, в котором запускается Задача, уничтожается при удалении экземпляра, поскольку фоновые потоки уничтожаются при завершении всех потоков переднего плана их владельца. Вот почему ваша задача начинается, но не заканчивается.

Если вы хотите, чтобы Задача переживала объект, обрабатывающий запрос, вы могли бы выполнить задачу и ее управление жизненным циклом stati c. Конечно, это не подойдет серверу, принимающему любое число потенциально одновременных запросов, поэтому задача stati c должна представлять собой набор Task, в который вы помещаете объект задачи. Мы только что ввели проблемы с параллелизмом, поэтому вам понадобится потокобезопасная очередь .

Как только вы начнете делать подобные вещи, вы берете на себя ответственность за жизненный цикл объекта, потому что он выиграл ' Сбор мусора не может производиться до тех пор, пока вы не удалите его из коллекции.

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

Честно говоря, я бы не стал использовать объект Task, я бы создал класс со свойствами для служебных деталей и метод, реализующий метод. поведения. Это комбинация шаблонов разработки Memento и Command.

Как уже упоминалось в другом ответе в надежном решении, ваши задачи выживут после перезагрузки сервера. Вы можете добиться этого, используя Memento / Command и постоянную очередь сообщений вместо очереди памяти. На Windows MSMQ доступен бесплатно. Преимущество этого способа заключается в том, что MSMQ берет на себя ответственность за безопасность потоков в управлении очередями.

Чтобы использовать внешнюю очередь сообщений, вам необходимо узнать о (де) сериализации. Другой ответ использует сервер базы данных, а не очередь сообщений, чтобы сохранить сериализованные сообщения, и это работает, но не масштабируется. Специальные очереди сообщений основаны на предположениях, которые нельзя сделать в универсальном ядре базы данных, и это позволяет им гораздо надежнее обрабатывать незапланированные простои и обрабатывать гораздо более высокие уровни параллелизма (или меньше нагружать сервер для данного уровня). траффи c).

1 голос
/ 30 марта 2020

Действие вашего контроллера должно возвращать Task<string> и помечаться async. Асинхронные методы, используемые в теле вашего действия, должны быть await ed.

Однако async/await предназначен для более коротких ожиданий, обычно сетевых запросов (например, базы данных или сетевой службы), а не в течение 45 минут. задания. Соединение браузера с клиентом достигнет времени ожидания через 1-2 минуты .

[HttpPost]
[ActionName("addJob")]
public async Task<string> AddJob([FromBody] Task task)
{
   // Add task ...

   await RemoveTaskAfterTime(task);

   return "Job has been added";
}

private async Task RemoveTaskAfterTime(Task task)
{
   System.Diagnostics.Debug.WriteLine("started to wait");
   await Task.Delay(5000);
   System.Diagnostics.Debug.WriteLine("remove task");
}
...