Оба класса System.Net.WebClient и System.Net.Http.HttpClient имеют асинхронную функцию. Это позволяет вам создать асинхронную функцию. Пока функция GetStringAsync работает асинхронно, вы можете регулярно проверять, запрашивается ли отмена.
Пример:
используя System.Net.Http;
класс HttpSonnetFetcher
{
const string sonnetsShakespeare = @ "http://www.gutenberg.org/cache/epub/1041/pg1041.txt";
public async Task<IEnumerable<string>> Fetch(CancellationToken token)
{
string bookShakespeareSonnets = null;
using (var downloader = new HttpClient())
{
var downloadTask = downloader.GetStringAsync(sonnetsShakespeare);
// wait until downloadTask finished, but regularly check if cancellation requested:
while (!downloadTask.Wait(TimeSpan.FromSeconds(0.2)))
{
token.ThrowIfCancellationRequested();
}
// if still here: downloadTask completed
bookShakespeareSonnets = downloadTask.Result;
}
// just for fun: find a nice sonnet, remove the beginning, split into lines and return 12 lines
var indexNiceSonnet = bookShakespeareSonnets.IndexOf("Shall I compare thee to a summer's day?");
return bookShakespeareSonnets.Remove(0, indexNiceSonnet)
.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Take(12);
}
}
Использование будет следующим:
private void TestCancellationHttpClient()
{
try
{
var sonnetFetcher = new HttpSonnetFetcher();
var cancellationTokenSource = new CancellationTokenSource();
var sonnetTask = Task.Run(() => sonnetFetcher.Fetch(cancellationTokenSource.Token));
cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10));
// meanwhile do something else, checking regularly if the task finished, or if you have nothing to do, just Task.Wait():
while (!sonnetTask.Wait(TimeSpan.FromSeconds(0.25)))
{
Console.Write('.');
}
// if still here: the sonnet is fetched. return value is in sonnetTask.Result
Console.WriteLine("A nice sonnet by William Shakespeare:");
foreach (var line in sonnetTask.Result)
{
Console.WriteLine(line);
}
}
catch (OperationCanceledException exc)
{
Console.WriteLine("Canceled " + exc.Message);
}
catch (AggregateException exc)
{
Console.WriteLine("Task reports exceptions");
var x = exc.Flatten();
foreach (var innerException in x.InnerExceptions)
{
Console.WriteLine(innerException.Message);
}
}
catch (Exception exc)
{
Console.WriteLine("Exception: " + exc.Message);
}
}
Попробуйте сделать это в простой консольной программе и убедитесь, что сонет получен правильно, уменьшите значение CancelAfter с 10 секунд, скажем, до 0,1 секунды, и убедитесь, что задача правильно отменена.
Примечание: хотя выдается исключение OperationCancelledException, это исключение рассматривается как внутреннее исключение AggregateException. Все исключения, возникающие в рамках задачи, всегда заключаются в AggregateException.