【问题标题】:Does WebClient.DownloadFileTaskAsync() never actually timeout?WebClient.DownloadFileTaskAsync() 真的不会超时吗?
【发布时间】:2026-02-04 03:00:02
【问题描述】:

在异步前的日子里,人们想知道如何在 WebClient 上设置超时,答案只是扩展基类并覆盖 GetWebRequest() 并在那里设置超时。

protected override WebRequest GetWebRequest(Uri address)
{
    // NOTE: this override has no affect if the Async methods are used!!!
    WebRequest request = base.GetWebRequest(address);
    ((HttpWebRequest)request).Timeout = 20 * 60 * 1000;
    ((HttpWebRequest)request).ReadWriteTimeout = 20 * 60 * 1000;
    return request;
}

假设人们需要更长的超时。

然后添加了xyzTaskAsync() 方法,人们想知道如何设置超时,答案是使用由本地计时器驱动的 CancellationToken。

所以我猜假设人们需要在给定时间后结束请求。

那么这是否意味着DownloadFileTaskAsync()DownloadStringTaskAsync()永远不会自己超时?超时失败不是任何网络操作的固有部分吗?

我使用了GetWebRequest() 覆盖来设置一个非常小的超时值。调用非异步方法时会抛出超时异常,而调用异步方法时则不会。

我已经反编译了 System.Net 库,但异步方法似乎调用了一些不易发现的缓存匿名 lambda。

是否有人确切地知道DownloadXyzTaskAsync() 方法是否以等效于无限超时值的方式执行?

【问题讨论】:

标签: c# .net async-await webclient webclient-download


【解决方案1】:

同步操作是阻塞的,这意味着线程在某种等待句柄上被阻塞。该等待可以是无限的(如果操作本身没有结束,那么该等待也可以是无限的)或者该等待可以接收某种超时,之后它会解除阻塞并超时。

异步操作本质上是异步的,没有任何活动部分在做某事。没有线程被阻塞或类似的东西。这意味着它本质上不能在没有任何东西的情况下真正超时或取消,即使这样,操作也只是被放弃而不是取消。这通常是CancellationToken(在超时后可能会或可能不会通过计时器发出信号)。

因此,这种(以及任何其他类型的)异步操作需要一些东西(即CancellationToken)才能超时。确实,该库可以在内部使用计时器,但在 .Net 中很少这样做,因为这是出乎意料的,您可以自己取消 CancellationToken

因此,在这种特定情况下,通常情况下,异步方法通常不受配置的超时的影响。 SocketTcpClientUdpClient等也是如此。

现在,如果您想从HttpWebRequest 的实际代码中进行确认,您可以看到超时用于创建定时器队列here。该队列用于在GetResponse 中创建超时计时器,但从不在BeginGetResponse 中创建。这是DownloadXXXAsync 中使用的异步选项,DownloadXXXTaskAsync 中使用。

【讨论】:

  • 感谢您指出我忘记的“开源”选项。 (我使用的是第三方反编译器。)我看到了对计时器队列的引用,这就是我怀疑的原因,但我现在清楚地看到它不是由方法的异步版本调用的。
  • 这将是一个全新的讨论,但作为旁注,我会认为超时条件通常是网络协议的固有部分,并且异步调用仍然必须遵守它。我想现在它是一个客户端选项,而不是服务器驱动的,这是有道理的。但它无处不在,不是吗。浏览器和下载客户端一直超时。
  • @mdisibio 如我所见,.Net 人员仅在您无法自己做事时(以合理的方式)为您提供 API。使内部同步操作超时是您无法从外部执行的操作。