使用语句中的异步方法

时间:2015-11-15 17:55:00

标签: c# asynchronous unity3d .net-3.5 using

注意:我在Unity中使用C#,这意味着.NET 3.5 版本,因此我无法使用awaitasync关键字..

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

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

如您所知,在调用DownloadFileAsync()方法后,SomeMethod1()将会被using块调用,而DownloadFileAsync()仍在工作< / strong>即可。所以现在我真的很困惑在这种情况下使用语句和异步方法会发生什么。

Dispose()的{​​{1}}会在合适的时间被调用而不会出现任何问题吗?

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

2 个答案:

答案 0 :(得分:21)

来自评论:

  

然后我该如何避免这种情况?只需添加await关键字?

不,你不能只是那样做。 (这就是为什么之前提出的重复问题实际上并不重复......你的情况略有不同。)你需要延迟处理,直到下载完成,但是由于你需要再执行两个程序语句,这很复杂(至少......如果没有a good, minimal, complete code example),就不可能确定。

认为你应该切换到等待的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,其他方法则要求您在Dispose()事件处理程序中调用DownloadXXXCompleted。如果没有更完整的代码示例,并且明确解释为什么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和/或“苗条”等效。我使用{{ 1}}只是因为它的简单性和效率,并且考虑到读者可以调整以适应他们喜欢的同步方式。一个明显的理由是人们可能更喜欢其他而不是Monitor。其他类型的同步技术不会冒Monitor事件处理程序本身阻止等待DownloadFileCompletedSomeMethod1()方法完成的风险。这是否重要当然取决于如何与文件下载相比,这些方法调用需要多长时间。)

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

Dispose()

答案 1 :(得分:6)

<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 --> <script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>提供事件System.Net.WebClient。您可以为该事件添加处理程序并在那时处置客户端。