Итак, у вас есть асинхронная c процедура, которая использует HttpClient для извлечения некоторой информации и обработки извлеченных данных:
async Task CallWebApiAsync() {...}
Улучшение 1 : рекомендуется суффиксировать asyn c методы с asyn c. Это сделано для того, чтобы позволить асин c версии существовать рядом с неасин c версией, которая делает что-то похожее.
Внутри этого метода вы используете один из методов HttpClient для выборки информация. Поскольку CallWebApiAsyn c является ожидаемым, я предполагаю, что используются методы asyn c (GetAsyn c, GetStreamAsyn c, et c), и что метод ожидает только тогда, когда ему нужен результат asyn c method.
Приятно то, что, как пользователь CallWebApiAsyn c, до тех пор, пока вы не ожидаете звонка, вы можете делать другие вещи, даже если веб-сайт не реагирует Проблема в том, что через 2 секунды вы хотите снова вызвать метод. Но что делать, если метод еще не завершен.
Улучшение 2 Поскольку вы хотите иметь возможность начать новую задачу, в то время как предыдущая еще не завершена: запомните запущенную задач, и выбросить их, когда закончите.
HashSet<Task> activeTasks = new HashSet<Task>(); // efficient add, lookup, and removal
void TaskStarted(Task startedTask)
{
// remember the startedTask
activeTasks.Add(startedTask);
}
void TaskCompleted(Task completedTask)
{
// If desired: log or process the results
LogFinishedTask(completedTask);
// Remove the completedTask from the set of ActiveTasks:
activeTasks.Remove(completedTask);
}
Может быть удобно удалить все выполненные задачи сразу:
void RemoveCompletedTasks()
{
var completedTasks = activeTasks.Where(task => task.IsCompleted).ToList();
foreach (var task in completedTasks)
{
TaskCompleted(completedTask);
}
}
Теперь мы можем настроить ваш ProcThread.
Улучшение 3 : в asyn c -aaait всегда возвращает Task
вместо void
и Task<TResult>
вместо TResult
. Единственное исключение: обработчики событий возвращают void
.
async Task ProcThread()
{
// Repeatedly: start a task; remember it, and wait 2 seconds
TimeSpan waitTime = TimeSpan.FromSeconds(2);
while (!terminationRequested)
{
Task taskWebApi = CallWebApiAsync();
// You didn't await, so you are free to do other things
// Remember the task that you started.
this.TaskStarted(taskWebApi);
// wait a while before you start new task:
await Task.Delay(waitTime);
// before starting a new task, remove all completed tasks
this.RemoveCompletedTasks();
}
}
Улучшение 4 : использование TimeSpan.
TimeSpan.FromSeconds(2)
гораздо проще понять, что оно представляет, чем значение 2000.
Как остановить?
Проблема, конечно, после того, как вы запросите завершение, могут все еще выполняться некоторые задачи. Вам придется ждать их до конца sh. Но даже тогда: некоторые задачи могут вообще не завершиться sh в течение разумного времени.
Улучшение 5 : используйте CancellationToken для запроса отмены.
Чтобы отменить задачи в аккуратно, класс CancellationToken
придуман. Пользователи, которые запускают задачу, создают объект CancellationTokenSource
и запрашивают у него CancellationToken
. Этот токен передается всем asyn c методам. Как только пользователь хочет отменить все задачи, которые были запущены с использованием этого CancellationTokenSource
, он просит CancellationTokenSource
отменить. Все задачи, имеющие токен из этого источника, обещали регулярно проверять токен, чтобы узнать, требуется ли отмена. Если это так, задача выполняет некоторую очистку (при необходимости) и возвращает.
Все сводится в один класс:
class Test1
{
private HttpClient httpClient = new HttpClient(...);
private HashSet<TTask> activeTasks = new HashSet<TTask>();
public async Task StartAsync(CancellationToken cancellationToken)
{
// repeated CallWebApiAsync until cancellation is requested
TimeSpan waitTime = TimeSpan.FromSeconds(2);
// repeat the following until OperationCancelled
try
{
while (true))
{
// stop if cancellation requested
cancellationToken.ThrowIfCancellationRequested();
var taskWebApi = this.CallWebApiAsync(cancellationToken);
this.activeTasks.Add(taskWebApi);
await Task.Delay(waitTime, cancellationToken);
// remove all completed tasks:
activeTasks.RemoveWhere(task => task.IsCompleted);
}
}
catch (OperationCanceledException exception)
{
// caller requested to cancel. Wait until all tasks are finished.
await Task.WhenAll(this.activeTasks);
// if desired do some logging for all tasks that were not completed.
}
}
И настроенный CallWebApiAsyn c:
private async Task CallWebApiAsync(CancellationToken cancellationToken)
{
const string requestUri = ...
var httpResponseMessage = await this.httpClient.GetAsync(requestUri, cancellationToken);
// if here: cancellation not requested
this.ProcessHttpResponse(httpResponseMessage);
}
private void ProcessHttpRespons(HttpResponseMessage httpResponseMessage)
{
...
}
}
Использование:
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
Test1 test = new Test1();
Task taskCallWebApiRepeatedly = test.StartAsync(cancellationTokenSource.Token);
// because you didn't await, you are free to do other things, while WebApi is called
// every 2 seconds
DoSomethingElse();
// you get bored. Request cancellation:
cancellationTokenSource.Cancel();
// of course you need to await until all tasks are finished:
await Task.Wait(taskCallWebApiRepeatedly);
Поскольку каждый обещает регулярно проверять, запрашивается ли отмена, вы уверены, что в течение разумного времени все задачи завершены, и вычистили свой беспорядок. Определение или «разумное время» является произвольным, но, скажем, менее 100 мсек c?