8

我正在尝试异步 CTP,4.5 版允许使用异步方法而无需编写 Begin/End 方法。

我的第一个探测是执行一个返回 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);
    });
}

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

在异步方法中,我使用了等待,因为该方法是异步的,所以需要使用 TasEx.Run 来创建一个执行代码的新线程。

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

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

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

事实上,我在没有 async 和 await 的情况下使用相同的代码,结果是一样的,主程序没有阻塞,一切都按我的预期工作。方法是这样的:

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

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

4

2 回答 2

4

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

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

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

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

所以,如果你让你的method01Async“等待”,也就是说,让它返回一个Task

private async Task method01Async()
{
    await Task.Run(/* whatever */);
}

然后,您可以从该btnAsync01_Click方法中等待它,如果您将其设为 `async:

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

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

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

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

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

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

是的,你是对的。

事实上,我在没有 async 和 await 的情况下使用相同的代码,结果是一样的,主程序没有阻塞,一切都按我的预期工作。

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


作为旁注,没有必要async在你的method01Async. 直接返回Task(或者不返回,如果你想保持它void-returning),将同样工作:

private Task method01Async()
{
    return Task.Run(/* whatever */);
}
于 2012-04-06T14:34:30.867 回答
1

在这两种情况下,您都没有真正使用异步,因为您没有等待原始呼叫。这是你应该如何做的:

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

private async Task method01Async()
{
    return await TaskEx.Run(() =>
    {
        UpdateTxtLog("Enter method01Async: " + System.DateTime.Now);
        Thread.Sleep(10000);
        UpdateTxtLog("exit method01Async: " + System.DateTime.Now);
    });
}

一旦你把它改成这个(重要的部分是await method01Async()在你的按钮点击事件中,它会在它退出后跳回到那里,并且你的“在 ethod01Async 之后:”文本日志应该显示一个十秒的延迟,就像你的“退出 method01Async”日志一样在method01Async方法中。

于 2012-04-06T12:53:19.883 回答