如何为HttpWebRequest定义更积极的超时?

Mar*_*tin 8 .net c# httpwebrequest async-await portable-class-library

在可移植类库中,我有以下方法将数据发布到特定的Url.该方法效果很好.但是我想指定一个更积极的超时(默认为100秒).

考虑到来自可移植类库的HttpWebRequest类没有Timeout属性,如何在超过几秒的时间内确保调用被放弃?

public async Task<HttpResponse> PostAsync(Uri uri, string data)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";

    using (Stream requestStream = await request.GetRequestStreamAsync())
    {
        byte[] postBytes = Encoding.UTF8.GetBytes(data);
        requestStream.Write(postBytes, 0, postBytes.Length);
    }

    HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
    return new HttpResponse(response.StatusCode, await new StreamReader(response.GetResponseStream()).ReadToEndAsync());
}
Run Code Online (Sandbox Code Playgroud)

L.B*_*L.B 23

下面的代码将返回一个HttpWebResponse,如果超时则返回null.

HttpWebResponse response = await TaskWithTimeout(request.GetResponseAsync(), 100);
if(response != null)
{
  ....
}
Run Code Online (Sandbox Code Playgroud)
Task<HttpWebResponse> TaskWithTimeout(Task<WebResponse> task, int duration)
{
    return Task.Factory.StartNew(() =>
    {
        bool b = task.Wait(duration);
        if (b) return (HttpWebResponse)task.Result;
        return null;
    });
}
Run Code Online (Sandbox Code Playgroud)

- 编辑 -

创建扩展方法会更好

public static class SOExtensions
{
    public static Task<T> WithTimeout<T>(this Task<T> task, int duration)
    {
        return Task.Factory.StartNew(() =>
        {
            bool b = task.Wait(duration);
            if (b) return task.Result;
            return default(T);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

用法是:

var response = (HttpWebResponse)await request.GetResponseAsync().WithTimeout(1000);
Run Code Online (Sandbox Code Playgroud)

--EDIT 2--

另一种方式

public async static Task<T> WithTimeout<T>(this Task<T> task, int duration)
{
    var retTask = await Task.WhenAny(task, Task.Delay(duration))
                            .ConfigureAwait(false);

    if (retTask is Task<T>) return task.Result;
    return default(T);
}
Run Code Online (Sandbox Code Playgroud)

  • 出于两个原因,这是一个非常糟糕的想法.它为每次通话使用一个线程.如果你有1000个并行调用,你将有1000个线程处于等待状态.我个人用这种技术将系统推到了地面.2.当操作"超时"时,Web请求和底层TCP连接将永远保持打开状态.我亲自驾驶另一个系统:)使用这种技术. (3认同)
  • @ Alon-Catz 1.每次通话都使用TASK.任务不是线程.TPL将尝试将任务分配给正在使用的系统的最佳线程数.2.我同意WebResponse需要妥善处理.这并不意味着这是一个糟糕的解决方案.只需添加一个using语句:`using(var response =(HttpWebResponse)await request.GetResponseAsync().WithTimeout(1000)){}` (2认同)