可取消的异步Web请求

时间:2019-11-10 09:08:41

标签: c# cancellation cancellationtokensource cancellation-token

我正在尝试创建一种通用方法,该方法将取消异步Web请求以及与之相关的任何其他操作。我发现了与此有关的另一个问题,例如this

我已经编写了一个帮助程序类来完成此任务。我在下面展示:

     public static class Helpers
    {
        public static async Task<string> GetJson(string url,
            CancellationTokenSource cancellationTokenSource,
            bool useSynchronizationContext = true)
        {
            try
            {
                var request = (HttpWebRequest)WebRequest.Create(url);
                string jsonStringResult;
                using (WebResponse response = await request.GetResponseAsync()
                    .WithCancellation(cancellationTokenSource.Token,
                    request.Abort, useSynchronizationContext))
                {
                    Stream dataStream = response.GetResponseStream();
                    StreamReader reader = new StreamReader(dataStream);
                    jsonStringResult = await reader.ReadToEndAsync();
                    reader.Close();
                    dataStream.Close();
                }

                cancellationTokenSource.Token.ThrowIfCancellationRequested();

                return jsonStringResult;
            }
            catch (Exception ex) when (ex is OperationCanceledException
                || ex is TaskCanceledException)
            {

            }
            catch (Exception ex) when (ex is WebException
                && ((WebException)ex).Status == WebExceptionStatus.RequestCanceled)
            {

            }
            catch (Exception ex)
            {
                //Any other exception
            }
            finally
            {
                cancellationTokenSource.Dispose();
            }
            return default;
        }


        public static async Task<T> WithCancellation<T>(this Task<T> task,
            CancellationToken cancellationToken, Action action,
            bool useSynchronizationContext)
        {
            using (cancellationToken.Register(action, useSynchronizationContext))
            {
                return await task;
            }
        }
    }

通知该行

cancellationTokenSource.Token.ThrowIfCancellationRequested();

在返回JSON字符串之前。

当操作被取消并且执行流程在线时

StreamReader reader = new StreamReader(dataStream);

例如,将执行下面的所有行(reader.Close()等),并且在执行ThrowIfCancelationRequested()时将引发异常-是正确的吗?我想念什么吗?

如果是这样,是否有办法立即取消所有内容?

感谢大家的回应,

在提供答案和所有非常有用的注释之后,我更新了实现。我在链接中使用HttpClient和扩展方法来执行一项任务,该任务实际上无法被取消,就像可以执行的那样-readAsStringAsync实际上已执行,因为它无法执行接受取消令牌。

public static class Helpers
    {
        public static async Task<string> GetJson(string url,CancellationToken cancellationToken)
        {
            try
            {
                string jsonStringResult;

                using (var client = new HttpClient())
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    using (var response = await client.GetAsync(url, cancellationToken))
                    {
                        jsonStringResult = await response.Content.ReadAsStringAsync().WithCancellation(cancellationToken);
                    }
                }

                return jsonStringResult;
            }
            catch (Exception ex) when (ex is OperationCanceledException)
            {

            }
            catch (Exception ex) when (ex is WebException exception && exception.Status == WebExceptionStatus.RequestCanceled)
            {

            }
            catch (Exception ex)
            {
                //LogException(ex);
            }
            return default;
        }

        public static Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
        {
            return task.IsCompleted 
                ? task: task.ContinueWith(completedTask => completedTask.GetAwaiter().GetResult(),cancellationToken,TaskContinuationOptions.ExecuteSynchronously,TaskScheduler.Default);
        }
    }

1 个答案:

答案 0 :(得分:1)

  

下面的所有行(reader.Close()等)将被执行,并且   当ThrowIfCancelationRequested()为   被处决-正确吗?我想念什么吗?

     

如果是这样,有没有办法立即取消所有内容?

首先,要取消的操作必须 明确 支持被取消。因此,您必须竭尽所能使用代码以某种方式将CancellationToken作为参数来执行某些操作。如果不可能,那么您就没有其他机会了。

这就是为什么将此概念称为 合作取消 的原因。因为几乎双方都应该意识到发生了取消事件。客户端应该知道该代码实际上已被取消,对于客户而言,仅知道取消请求是不够的。对于被呼叫者来说,重要的是要知道为了成功完成请求已请求取消的事实。

关于在执行Closestream的{​​{1}}方法时检查操作是否被取消。为了避免内存泄漏,无论是否取消操作,您都必须始终调用清除方法。当然,我建议您使用reader语句,它将自动进行清理。

顺便说一句,为了使某些功能可取消,您不必在执行每一行之前检查是否请求取消。您只需要在执行一些长时间运行的操作之前和之后检查是否请求取消即可。如果长期运行的操作支持通过取消标记属性进行取消,则传递取消标记。

此外,您还必须查看副作用。如果您已经产生了副作用,说明您的方法不准备在出路时恢复原状,这将使您处于不一致状态,请不要取消。

一些通用代码块可能是这样的:

using

作为解决方案,可以使用if(ct.IsCancellationRequested) { break; // or throw } await DoSomething(ct); if (ct.IsCancellationRequested) { // if there is no side-effect return; // or throw // or, we already did something in `DoSomething` method // do some rollback } HttpClient之类的不同对象来执行异步,可等待和可取消的Web请求。您可以查看that link了解实施细节。