当方法返回void时,是否与任务相同?

Álv*_*cía 8 c# asynchronous async-ctp

我正在尝试异步CTP,即允许使用异步方法而不需要编写Begin/End方法的版本4.5.

我的第一个探测是执行返回void的异步方法.我看了几个例子并做了以下事情:

private void btnAsync01_Click(object sender, RoutedEventArgs e)
{
    UpdateTxtLog("click button: " + System.DateTime.Now);
    method01Async();
    UpdateTxtLog("after ethod01Async: " + System.DateTime.Now);
}

private async void method01Async()
{
    await TaskEx.Run(() =>
    {
        UpdateTxtLog("Enter method01Async: " + System.DateTime.Now);
        Thread.Sleep(10000);
        UpdateTxtLog("exit method01Async: " + System.DateTime.Now);
    });
}
Run Code Online (Sandbox Code Playgroud)

在我的WPF项目中,我有一个textBox,用于查看结果和一个执行异步方法的按钮.

在async方法中,我使用await,这是需要的,因为该方法是异步的,并且TasEx.Run创建一个新的线程,在其中执行代码.

我怀疑是在这一点上.在我看到的如何创建返回void的异步方法的几个示例中,使用这种方式,Task.Run或TaskEx.Run.

如果我没有错,Task.Run会创建一个新线程来执行该方法.那么为什么要使用异步方法,如果使用Task,创建一个新线程,我得到我想要的,而不是阻塞主线程?

另外,如果异步方法访问某些共享变量,我必须小心并发,对吧?所以我不知道使用异步方法的优势,至少在这种情况下.

事实上,我使用相同的代码没有异步,没有等待,结果是相同的,主程序没有阻塞,所有工作正如我所料.方法是这样的:

private void method01Async()
{
    TaskEx.Run(() =>
    {
        UpdateTxtLog("Enter method01Async: " + System.DateTime.Now);
        Thread.Sleep(10000);
        UpdateTxtLog("Exit method01Async: " + System.DateTime.Now);
    });
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,当方法返回void时,这是使用async的正确方法吗?

svi*_*ick 4

\n

如果我没记错的话,Task.Run 创建一个新线程来执行该方法。

\n
\n\n

不完全是。Task.Run()将在与 UI 线程不同的线程上运行代码(至少使用默认线程TaskScheduler)。但在大多数情况下,它实际上不会创建新线程,而是会重用ThreadPool.

\n\n
\n

那为什么要使用异步方法,如果用Task,创建一个新线程,我就得到了我想要的,而不是阻塞主线程呢?

\n
\n\n

在 UI 应用程序的上下文中,要点async是能够在异步操作完成后轻松地在 UI 线程上执行一些代码。

\n\n

所以,如果你让你的method01Async\xe2\x80\x9cawaitable\xe2\x80\x9d ,也就是说,让它返回一个Task

\n\n
private async Task method01Async()\n{\n    await Task.Run(/* whatever */);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

btnAsync01_Click如果你将其设置为“异步”,那么你可以从方法中等待它:

\n\n
private async void btnAsync01_Click(object sender, RoutedEventArgs e)\n{\n    UpdateTxtLog("click button: " + System.DateTime.Now);\n    await method01Async();\n    UpdateTxtLog("after method01Async: " + System.DateTime.Now);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这样,只有在Taskinmethod01Async执行完毕后,才会执行该方法的最后一行。并且它将在 UI 线程上执行。

\n\n

在 .Net 4.0 中,您可以使用ContinueWith()和实现类似的效果Dispatcher.Invoke()

\n\n
private void btnAsync01_Click(object sender, RoutedEventArgs e)\n{\n    UpdateTxtLog("click button: " + System.DateTime.Now);\n    method01Async().ContinueWith(() =>\n        Dispatcher.Invoke(\n            new Action(() =>\n                UpdateTxtLog("after method01Async: " + System.DateTime.Now)));\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我相信你会同意这更加混乱且可读性较差。

\n\n
\n

另外,如果异步方法访问某些共享变量,我必须小心并发性,对吗?

\n
\n\n

是的,你是对的。

\n\n
\n

事实上,我使用相同的代码,没有异步和等待,结果是相同的,主程序没有阻塞,并且一切都按我的预期工作。

\n
\n\n

结果肯定不是我认为你的代码应该做的。,的最后一行btnAsync01_Click将在 method01Async\xe2\x80\x9d 之后执行 \xe2\x80\x9c,但不会等到Task该方法中的启动完成。

\n\n
\n\n

作为旁注,无需async在您的method01Async. 直接返回Task(或者不返回,如果你想保留它void返回),将起到相同的作用:

\n\n
private Task method01Async()\n{\n    return Task.Run(/* whatever */);\n}\n
Run Code Online (Sandbox Code Playgroud)\n