C#示例中的异步和等待

4
我在Microsoft网站上找到了有关异步和等待的以下示例(https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/):
// Three things to note in the signature:  
//  - The method has an async modifier.   
//  - The return type is Task or Task<T>. (See "Return Types" section.)  
//    Here, it is Task<int> because the return statement returns an integer.  
//  - The method name ends in "Async."  
async Task<int> AccessTheWebAsync()  
{   
    // You need to add a reference to System.Net.Http to declare client.  
    HttpClient client = new HttpClient();  

    // GetStringAsync returns a Task<string>. That means that when you await the  
    // task you'll get a string (urlContents).  
    Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com");  

    // You can do work here that doesn't rely on the string from GetStringAsync.  
    DoIndependentWork();  

    // The await operator suspends AccessTheWebAsync.  
    //  - AccessTheWebAsync can't continue until getStringTask is complete.  
    //  - Meanwhile, control returns to the caller of AccessTheWebAsync.  
    //  - Control resumes here when getStringTask is complete.   
    //  - The await operator then retrieves the string result from getStringTask.  
    string urlContents = await getStringTask;  

    // The return statement specifies an integer result.  
    // Any methods that are awaiting AccessTheWebAsync retrieve the length value.  
    return urlContents.Length;  
}  

我的第一个问题来自网页“If AccessTheWebAsync在调用GetStringAsync并等待其完成之间没有任何工作可做,则可以通过调用以下单个语句并等待来简化代码。” 这似乎意味着以下语句确实在执行某些操作:

    Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com");  

这是真的吗?我认为它只是一个任务的定义,但还没有开始/运行。

第二个问题是,我尝试更改实现,以检查我的想法:

async Task<int> AccessTheWebAsync()  
{    
    HttpClient client = new HttpClient();  

    //Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com");
        Task<string> getStringTask = DoPriorWork();  

    // You can do work here that doesn't rely on the string from GetStringAsync.  
    DoIndependentWork();  

    string urlContents = await getStringTask;  

    return urlContents.Length;  
}  
void DoIndependentWork()
    {
        resultsTextBox.Text += "Working . . . . . . .\r\n";
    }

    Task<string> DoPriorWork()
    {
        return new Task<string>(
            () =>
            {
                string retStr = "good";
                Debug.Assert(true, "running dopriorwork");
                System.Threading.Thread.Sleep(10000);
                return retStr;
            }
        );
    }

然而,方法DoPriorWork()根本没有执行。这个实现有什么问题吗?谢谢!

感谢GSerg的评论。您也能回答我的第二个问题吗? - Ames ISU
1
不要将阻塞线程的 Thread.Sleep 与异步任务混淆,应该使用 Task.Delay,因为它会延迟任务而不阻塞线程。 - Hans Kesting
尝试 async Task<string> DoPriorWork() { await Task.Delay(10000); return "good";} - Hans Kesting
1
GetStringAsync返回一个已启动的任务。它已经在运行,所以当等待它时,最终会返回结果。你使用了任务构造函数创建了一个未启动的任务。由于它没有启动,所以当等待它时永远不会完成。一般来说,API返回已启动的任务,除非你真的知道自己在做什么,否则不应该使用任务构造函数。请改用Task.Run。 - Mike Zboray
啊,Task(Func<TResult>) 构造函数 返回一个未启动的任务。你需要手动调用 Start() 方法来启动它。 - GSerg
显示剩余2条评论
2个回答

1

这是真的吗?我认为这只是一个任务的定义,但它尚未开始/运行。

确实。您调用的操作立即返回,您获得的任务是“热”的 - 即已经在运行。这就是为什么您实际上会得到该任务,以便稍后等待它。因此,您可以运行其他内容,就像示例所述。

第二个问题是我尝试更改实现方式以检查我的想法...

为了使代码能够运行,请尝试将方法DoPriorWork更改如下:

private Task<string> DoPriorWork()
{
    return Task.Run(() =>
    {
        var retStr = "good";
        Console.WriteLine("running dopriorwork");
        Thread.Sleep(10000);
        return retStr;
    });
}

请注意,我不是返回一个新的Task实例,这会让我得到一个处于Created(未运行)状态的任务,而是使用静态方法Task.Run()来排队并返回一个“热”任务。
希望这有所帮助!

2
使用 Task.Delay 代替 sleep。请参见 相关链接 - Hans Kesting
据我理解,在这种情况下,这并不重要。如果 OP 使用 Thread.Sleep(),当前线程将被阻塞,而如果使用 Task.Delay(),他必须等待该任务完成以实现暂停。因此,我认为这样做没有太多好处。我错了吗? - Karel Tamayo

0

从同一个链接中,如果你看一下这个图表,就能够理解流程控制。

对于这一行代码:

Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com"); 

控制流进入GetStringAsync方法(正常处理),直到在该方法中遇到await,才会返回给调用者,如图所示。

至于这个语句:

“如果AccessTheWebAsync在调用GetStringAsync和等待其完成之间没有任何工作可做,则可以通过在以下单个语句中调用并等待来简化代码。”

这是指示例中的DoIndependentWork()在等待定义的getStringTask任务之前执行的情况。

这个语句的意思是,在不需要DoIndependentWork()的情况下,可以使用以下语句代替:

Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com"); 
DoIndenpendentWork();
string urlContents = await getStringTask; 

你可以尝试以下方法:

string urlContents = await client.GetStringAsync("https://msdn.microsoft.com"); 

这个有意义吗?


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