Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 支持CancellationToken实现扩展方法WebRequest.GetResponseAsync_.net_Asynchronous_Httpwebrequest_Task Parallel Library_Cancellation - Fatal编程技术网

.net 支持CancellationToken实现扩展方法WebRequest.GetResponseAsync

.net 支持CancellationToken实现扩展方法WebRequest.GetResponseAsync,.net,asynchronous,httpwebrequest,task-parallel-library,cancellation,.net,Asynchronous,Httpwebrequest,Task Parallel Library,Cancellation,这里的想法很简单,但是实现有一些有趣的细微差别。这是我想在.net4中实现的扩展方法的签名 public static Task<WebResponse> GetResponseAsync(this WebRequest request, CancellationToken token); 我的问题很简单,但很有挑战性。当与TPL一起使用时,该实现是否真的会像预期的那样工作 当与TPL一起使用时,该实现是否真的会像预期的那样工作 没有 它不会将任务结果标记为已取消,因此行为将不完全

这里的想法很简单,但是实现有一些有趣的细微差别。这是我想在.net4中实现的扩展方法的签名

public static Task<WebResponse> GetResponseAsync(this WebRequest request, CancellationToken token);
我的问题很简单,但很有挑战性。当与TPL一起使用时,该实现是否真的会像预期的那样工作

当与TPL一起使用时,该实现是否真的会像预期的那样工作

没有

  • 它不会将
    任务
    结果标记为已取消,因此行为将不完全符合预期
  • 在超时的情况下,由
    任务报告的
    aggregateeexception
    中包含的
    WebException
    将具有状态
    WebExceptionStatus.requestcancelled
    。它应该是
    WebExceptionStatus.Timeout
  • 实际上,我建议使用
    TaskCompletionSource
    来实现这一点。这允许您编写代码,而无需创建自己的APM样式方法:

    public static Task<WebResponse> GetResponseAsync(this WebRequest request, CancellationToken token)
    {
        if (request == null)
            throw new ArgumentNullException("request");
    
        bool timeout = false;
        TaskCompletionSource<WebResponse> completionSource = new TaskCompletionSource<WebResponse>();
    
        AsyncCallback completedCallback =
            result =>
            {
                try
                {
                    completionSource.TrySetResult(request.EndGetResponse(result));
                }
                catch (WebException ex)
                {
                    if (timeout)
                        completionSource.TrySetException(new WebException("No response was received during the time-out period for a request.", WebExceptionStatus.Timeout));
                    else if (token.IsCancellationRequested)
                        completionSource.TrySetCanceled();
                    else
                        completionSource.TrySetException(ex);
                }
                catch (Exception ex)
                {
                    completionSource.TrySetException(ex);
                }
            };
    
        IAsyncResult asyncResult = request.BeginGetResponse(completedCallback, null);
        if (!asyncResult.IsCompleted)
        {
            if (request.Timeout != Timeout.Infinite)
            {
                WaitOrTimerCallback timedOutCallback =
                    (object state, bool timedOut) =>
                    {
                        if (timedOut)
                        {
                            timeout = true;
                            request.Abort();
                        }
                    };
    
                ThreadPool.RegisterWaitForSingleObject(asyncResult.AsyncWaitHandle, timedOutCallback, null, request.Timeout, true);
            }
    
            if (token != CancellationToken.None)
            {
                WaitOrTimerCallback cancelledCallback =
                    (object state, bool timedOut) =>
                    {
                        if (token.IsCancellationRequested)
                            request.Abort();
                    };
    
                ThreadPool.RegisterWaitForSingleObject(token.WaitHandle, cancelledCallback, null, Timeout.Infinite, true);
            }
        }
    
        return completionSource.Task;
    }
    

    @280Z28谢谢-我在没有VS的情况下写了这篇文章,所以无法实际测试所有内容;)@280Z28是的-由于我无法测试它,我没有意识到,
    Abort
    仍然会触发回调(这是有道理的)。这只会导致行为有点不正常,但仍然正常。(你将得到一个WebException,而不是正确的取消)。我将你的帖子编辑为1)正确描述我原始问题中的2个主要错误(包括1个新错误),2)包含最新的工作代码,3)包含一个测试类,在成功案例和3个不同的失败案例(取消、超时和404错误)下显示正确的行为@280Z28谢谢!这看起来像是一个合理的、全面的实现。非常感谢大家!不过有一点值得注意。实际上只有一个unittest正在测试新的扩展方法。其他的只是测试框架功能,因为它们不传入cancellationtoken。
    public static Task<WebResponse> GetResponseAsync(this WebRequest request, CancellationToken token)
    {
        if (request == null)
            throw new ArgumentNullException("request");
    
        bool timeout = false;
        TaskCompletionSource<WebResponse> completionSource = new TaskCompletionSource<WebResponse>();
    
        AsyncCallback completedCallback =
            result =>
            {
                try
                {
                    completionSource.TrySetResult(request.EndGetResponse(result));
                }
                catch (WebException ex)
                {
                    if (timeout)
                        completionSource.TrySetException(new WebException("No response was received during the time-out period for a request.", WebExceptionStatus.Timeout));
                    else if (token.IsCancellationRequested)
                        completionSource.TrySetCanceled();
                    else
                        completionSource.TrySetException(ex);
                }
                catch (Exception ex)
                {
                    completionSource.TrySetException(ex);
                }
            };
    
        IAsyncResult asyncResult = request.BeginGetResponse(completedCallback, null);
        if (!asyncResult.IsCompleted)
        {
            if (request.Timeout != Timeout.Infinite)
            {
                WaitOrTimerCallback timedOutCallback =
                    (object state, bool timedOut) =>
                    {
                        if (timedOut)
                        {
                            timeout = true;
                            request.Abort();
                        }
                    };
    
                ThreadPool.RegisterWaitForSingleObject(asyncResult.AsyncWaitHandle, timedOutCallback, null, request.Timeout, true);
            }
    
            if (token != CancellationToken.None)
            {
                WaitOrTimerCallback cancelledCallback =
                    (object state, bool timedOut) =>
                    {
                        if (token.IsCancellationRequested)
                            request.Abort();
                    };
    
                ThreadPool.RegisterWaitForSingleObject(token.WaitHandle, cancelledCallback, null, Timeout.Infinite, true);
            }
        }
    
        return completionSource.Task;
    }
    
    [TestClass]
    public class AsyncWebRequestTests
    {
        [TestMethod]
        public void TestAsyncWebRequest()
        {
            Uri uri = new Uri("http://google.com");
            WebRequest request = HttpWebRequest.Create(uri);
            Task<WebResponse> response = request.GetResponseAsync();
            response.Wait();
        }
    
        [TestMethod]
        public void TestAsyncWebRequestTimeout()
        {
            Uri uri = new Uri("http://google.com");
            WebRequest request = HttpWebRequest.Create(uri);
            request.Timeout = 0;
            Task<WebResponse> response = request.GetResponseAsync();
            try
            {
                response.Wait();
                Assert.Fail("Expected an exception");
            }
            catch (AggregateException exception)
            {
                Assert.AreEqual(TaskStatus.Faulted, response.Status);
    
                ReadOnlyCollection<Exception> exceptions = exception.InnerExceptions;
                Assert.AreEqual(1, exceptions.Count);
                Assert.IsInstanceOfType(exceptions[0], typeof(WebException));
    
                WebException webException = (WebException)exceptions[0];
                Assert.AreEqual(WebExceptionStatus.Timeout, webException.Status);
            }
        }
    
        [TestMethod]
        public void TestAsyncWebRequestCancellation()
        {
            Uri uri = new Uri("http://google.com");
            WebRequest request = HttpWebRequest.Create(uri);
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
            Task<WebResponse> response = request.GetResponseAsync(cancellationTokenSource.Token);
            cancellationTokenSource.Cancel();
            try
            {
                response.Wait();
                Assert.Fail("Expected an exception");
            }
            catch (AggregateException exception)
            {
                Assert.AreEqual(TaskStatus.Canceled, response.Status);
    
                ReadOnlyCollection<Exception> exceptions = exception.InnerExceptions;
                Assert.AreEqual(1, exceptions.Count);
                Assert.IsInstanceOfType(exceptions[0], typeof(OperationCanceledException));
            }
        }
    
        [TestMethod]
        public void TestAsyncWebRequestError()
        {
            Uri uri = new Uri("http://google.com/fail");
            WebRequest request = HttpWebRequest.Create(uri);
            Task<WebResponse> response = request.GetResponseAsync();
            try
            {
                response.Wait();
                Assert.Fail("Expected an exception");
            }
            catch (AggregateException exception)
            {
                Assert.AreEqual(TaskStatus.Faulted, response.Status);
    
                ReadOnlyCollection<Exception> exceptions = exception.InnerExceptions;
                Assert.AreEqual(1, exceptions.Count);
                Assert.IsInstanceOfType(exceptions[0], typeof(WebException));
    
                WebException webException = (WebException)exceptions[0];
                Assert.AreEqual(HttpStatusCode.NotFound, ((HttpWebResponse)webException.Response).StatusCode);
            }
        }
    }