使用语句中的异步方法

注意:我在Unity中使用C#,这意味着.NET 3.5版本,所以我不能使用awaitasync关键字..

当我在其中放入一个异步工作的方法时, 使用语句会发生什么?

 using (WebClient wc = new WebClient()) { wc.DownloadFileAsync(urlUri, outputFile); } SomeMethod1(); SomeMethod2(); 

如您所知,在调用DownloadFileAsync()方法之后,将调用SomeMethod1() ,它不在using块中,而DownloadFileAsync()仍然有效 。 所以现在我真的很困惑在这种情况下使用语句和异步方法会发生什么。

是否会在正确的时间调用wc Dispose()而没有任何问题?

如果没有,我该如何更正此示例?

来自评论:

那怎么办呢? 只需添加await关键字?

不,你不能这样做。 (这就是为什么之前提出的重复问题实际上并不重复……你的情况略有不同。)你需要延迟处理直到下载完成,但是由于你需要再执行两个程序语句,这很复杂(至少……如果没有一个好的, 最小的完整的代码示例,就不可能确定。

我认为你应该切换到等待的WebClient.DownloadFileTaskAsync()方法,因为这至少会简化实现,使得保留using语句变得简单。

您可以通过捕获返回的Task对象来解决问题的其他部分,直到执行其他程序语句之后才等待它:

 using (WebClient wc = new WebClient()) { Task task = wc.DownloadFileTaskAsync(urlUri, outputFile); SomeMethod1(); SomeMethod2(); await task; } 

通过这种方式,可以启动下载,调用其他两个方法, 然后代码将等待下载完成。 只有在它完成后才会退出using块,允许处理WebClient对象。

当然,在您当前的实现中,您无疑正在处理适当的DownloadXXXCompleted事件。 如果需要,可以继续使用该方式。 但恕我直言,一旦你切换到使用await ,只需在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); } } 

(注意:可以使用上面任何合适的同步,例如ManualResetEventCountdownEvent ,甚至Semaphore和/或“slim”等效。我使用Monitor只是因为它的简单性和效率,并且考虑到读者可以调整以适应他们的首选一种显而易见的原因是人们可能更喜欢Monitor以外的其他东西,其他类型的同步技术不会冒着让DownloadFileCompleted事件处理程序本身阻塞等待SomeMethod1()SomeMethod2()方法完成的SomeMethod2() 。这是否重要当然取决于与文件下载相比这些方法调用需要多长时间。)

但是,上面将阻止当前线程。 在某些情况下,这可能没问题,但大多数情况下,操作是在UI线程中启动的,并且在操作期间不应该阻止该线程。 在这种情况下,您将完全放弃using ,只需从完成事件处理程序中调用Dispose()

 WebClient wc = new WebClient(); wc.DownloadFileCompleted += (sender, e) => { wc.Dispose(); }; wc.DownloadFileAsync(urlUri, outputFile); SomeMethod1(); SomeMethod2(); 

System.Net.WebClient提供事件DownloadFileCompleted 。 您可以为该事件添加处理程序并在此时处置客户端。