根据评论:
那我怎么避免这个问题?只需要添加await关键字吗?
不,你不能仅仅这样做。(这就是为什么之前提出的重复问题实际上并不是重复的…你的情况略有不同。) 你需要延迟处理(dispose)直到下载完成,但由于你需要执行另外两条程序语句(至少如此…如果没有好的、最小化的、完整的代码示例是无法确定的)这会变得很麻烦。
我认为你应该转换为可等待(async-await)的 WebClient.DownloadFileTaskAsync()
方法,这将简化实现过程,使保持 using
语句成为可能。
你可以通过捕获返回的Task
对象,并在其他程序语句执行后再进行等待(await),来解决问题的另一个部分:
using (WebClient wc = new WebClient()) {
Task task = wc.DownloadFileTaskAsync(urlUri, outputFile);
SomeMethod1();
SomeMethod2();
await task;
}
通过这种方式,下载可以开始,您的其他两种方法被调用,然后代码将等待下载完成。只有在下载完成后,using
块才会退出,允许WebClient
对象被处理。
当然,在您当前的实现中,您无疑正在处理适当的DownloadXXXCompleted
事件。如果您愿意,您仍然可以继续使用该对象。但我认为,一旦您转而使用await
,最好在操作完成后直接放置需要执行的代码。这样可以将与操作相关的所有代码放在一个地方,并简化实现。
如果由于某种原因您无法使用await
,那么您将不得不使用一些替代机制来延迟处理WebClient
。一些方法将允许您继续使用using
,而另一些则要求您在DownloadXXXCompleted
事件处理程序中调用Dispose()
。如果没有更完整的代码示例和明确说明为什么await
不适合,就无法确定最佳的替代方案。
编辑:
既然您确认当前代码中没有await
,那么还有几个与旧代码兼容的选项…
一种可能性是在启动操作后在同一线程中等待:
using (WebClient wc = new WebClient()) {
object waitObject = new object();
lock (waitObject)
{
wc.DownloadFileCompleted += (sender, e) =>
{
lock (waitObject) Monitor.Pulse(waitObject);
};
wc.DownloadFileAsync(urlUri, outputFile);
SomeMethod1();
SomeMethod2();
Monitor.Wait(waitObject);
}
}
(注意: 可以使用任何适当的同步方式,例如ManualResetEvent
、CountdownEvent
,甚至是Semaphore
和/或“slim”等效类。我之所以使用Monitor
,只是因为它简单高效,并且假设读者可以根据自己的喜好调整来适应自己的同步手段。显然,一个人可能更喜欢使用Monitor
以外的某些同步技术,原因是其他类型的同步技术不会出现DownloadFileCompleted
事件处理程序本身阻塞等待SomeMethod1()
和SomeMethod2()
方法完成的风险。当然,这是否重要取决于这些方法调用需要多长时间才能与文件下载相比较。)
然而,上述方法将阻塞当前线程,在某些情况下这可能是可以接受的,但往往操作是在UI线程中启动的,该线程在操作期间不应被阻塞。在这种情况下,您将想放弃using
,并仅从完成事件处理程序中调用Dispose()
:
WebClient wc = new WebClient();
wc.DownloadFileCompleted += (sender, e) =>
{
wc.Dispose();
};
wc.DownloadFileAsync(urlUri, outputFile);
SomeMethod1();
SomeMethod2();
*TaskAsync
与await
。 - SLaksawait
,所以存在问题。 - Yacoub MassadWebClient.DownloadFileTaskAsync()
,则可以使用重复中提出的内容。 - jdphenix