使用语句中的异步方法

Jen*_*nix 25 c# asynchronous using unity-game-engine .net-3.5

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

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

using (WebClient wc = new WebClient()) {
    wc.DownloadFileAsync(urlUri, outputFile);
}
SomeMethod1();
SomeMethod2();
Run Code Online (Sandbox Code Playgroud)

如您所知,在DownloadFileAsync()调用该方法之后,SomeMethod1()将在仍在工作using时调用哪个是块外的.所以现在我真的很困惑在这种情况下using语句和异步方法会发生什么.DownloadFileAsync()

Dispose()wc,在没有任何问题的正确的时间叫什么名字?

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

Pet*_*iho 22

来自评论:

那我怎么避免这个呢?只需添加await关键字?

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

认为你应该切换到awaitable WebClient.DownloadFileTaskAsync()方法,因为这将至少简化的实施,使得它很容易保留using声明.

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

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

通过这种方式,可以启动下载,调用其他两个方法,然后代码将等待下载完成.只有当它完成时才会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);
    }
}
Run Code Online (Sandbox Code Playgroud)

(注意:可以使用上面的任何合适的同步,例如ManualResetEvent,CountdownEvent甚至Semaphore和/或"苗条"等同物.我使用它Monitor只是因为它的简单性和效率,并且考虑到读者可以调整以适应他们喜欢的同步方式.一个明显的原因人们可能会喜欢的东西其他Monitor的是,其他类型的同步技术将无法运行具有的风险DownloadFileCompleted事件处理程序本身阻塞等待的SomeMethod1()SomeMethod2()方法来完成,这是否是重要的当然是多久这些方法取决于与文件下载相比,将进行调用.)

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

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


pil*_*cam 6

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