在if条件语句中调用async Task<bool>方法

9
我想知道以下代码是否会等待异步方法完成后再执行主线程,还是只会在条件上继续执行主线程并将方法返回视为false。
public async Task<bool> SomeMethod
{
    if(await AsyncMethod(param))
    {
        //Do something
    }
}

异步方法定义如下:

public async Task<bool> AsyncMethod(SomeClass param)
{
   //Do something
}

1
你在这里使用了 await 关键字,所以它应该等待函数返回,为什么你觉得它不会等呢? - CodingGorilla
@CodingGorilla 嗯,它不会这样做。一旦遇到await,它就会立即返回。这就是await重点,它是异步的,而不是同步等待。 - Servy
@Servy 但是在收到AsyncMethod的结果之前,它不应继续执行SomeMethod的其余部分。换句话说,每当调度SomeMethod时,除非它已经收到对AsyncMethod的调用结果,否则它不应进入或通过if - CodingGorilla
@CodingGorilla 这是正确的,而且与你第一次说的完全不同。 - Servy
@Servy 好的,我承认,我表达那个问题不太好。 - CodingGorilla
只是一个未被执行的代码块让我认为它可以在主线程中运行。但是我重新阅读了MSDN,发现await应该等待。谢谢你快速的回复。 - liferunsoncode
4个回答

18

以下代码会等待异步方法完成后再执行主线程,还是仅继续执行主线程且将方法返回值视为false呢?

都不是。

await 是一种“异步等待”。换句话说,方法会等待,但线程不会。

当你的方法遇到那个 await(假设它确实有些等待要做),它会立即向调用者返回一个未完成的任务。线程继续执行它想要做的任何操作。稍后,当被等待的任务完成时,SomeMethod 将恢复执行。当 SomeMethod 完成时,它之前返回的任务也会完成。

我在我的博客文章中详细介绍了这个主题。


6
它将等待操作完成。
注意您如何调用操作:
if(await AsyncMethod(param))

两件事:

  1. 使用 await 关键字会等待操作完成,这一点毫不意外。
  2. 如果它没有等待操作完成,那么就会出现编译错误。因为虽然可以在条件语句中使用 bool,但不能使用 Task<bool>

1
等待一个异步方法会导致调用线程从方法调用者返回,直到异步方法完成。一旦方法完成,在同步上下文中,调用线程将切换回离开的位置。没有同步上下文,await方法仍然会返回给调用者,并且调用方法的执行将恢复(但不一定在最初调用的线程上)。 这将发生无论返回类型/值如何。
在您的情况下(假设语法正确)。SomeMethod(您需要添加())将调用AsyncMethod并立即返回。一旦AsyncMethod完成,SomeMethod将切换回离开的位置,即评估返回的值(true或false)。如果值为true,则调用//Do something,否则它将跳过if语句并完成执行SomeMethod。
以下是没有同步上下文的示例。注意,“await getStringTask”后线程ID的更改以及GetValue的其余部分在“新”线程上执行。
    private static void Main(string[] args)
    {
        GetValue();
        Console.WriteLine("test1");
        Console.WriteLine("1 " + Thread.CurrentThread.ManagedThreadId);
        Console.ReadKey();
    }

    private static async void GetValue()
    {
        Console.WriteLine(await AccessTheWebAsync());
        Console.WriteLine("2 " + Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("test2");
        Console.WriteLine("3 " + Thread.CurrentThread.ManagedThreadId);
    }

    private static async Task<int> AccessTheWebAsync()
    {
        HttpClient client = new HttpClient();
        Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
        Console.WriteLine("4 " + Thread.CurrentThread.ManagedThreadId);
        string urlContents = await getStringTask;
        Console.WriteLine("5 " + Thread.CurrentThread.ManagedThreadId);
        return urlContents.Length;
    }

打印
4 9
test1
1 9
5 13
47984
2 13
test2
3 13

1
一旦方法完成,调用线程将切换回它离开的地方。这可能会发生,因为同步上下文的结果,但这并不一定发生。这不是await固有的特性。SomeMethod将在ThreadPool上调用AsyncMethod没有显示任何代码表明这一点。AsyncMethod是异步的,但这并不意味着线程池线程一定参与其中。 - Servy
根据我的经验,一旦一个等待的方法完成,调用线程的上下文会切换回原来离开的地方。能否解释一个情况,在这种情况下不会发生切换呢? - Tom
如果您唯一的使用await经验是在同步上下文中的UI环境中,那么是的,在该情况下,您会看到这种情况,但这不总是如此。并非所有情况下都会有同步上下文。就线程池而言,有许多创建异步的方法,使用单独的线程只是其中之一。另一个极其常见的操作是IO。 - Servy
我知道它可能无法在单独的线程上执行,并且我已经编辑了我的答案以使之正确。你说它不一定会回到离开的地方,我想知道在什么情况下会发生这种情况(除了程序在完成之前结束)。 - Tom
“async” 方法将总是在解析可等待对象为其值后立即返回给其调用者,这不是您引用并评论的语句。调用线程处理继续执行的语句有时是正确的,但并非总是如此。 - Servy
谢谢@Servy,我已经更新了我的答案以确保正确性,并添加了一个在非同步上下文中它将如何行为的示例。 - Tom

-1
你可以像这样同步地完成它:
if(AsyncMethod(param).Result)
{
    //Do something
}

希望它有所帮助 ;)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接