为什么我不能等待异步方法的返回值?

时间:2015-09-24 13:37:22

标签: c# .net asynchronous async-await

情况

我将raspberry pi设置为服务器,通过HTTP将json作为输入。 " API"允许设置连接到pi的LED。一切正常,我可以从浏览器发送请求,一切都很棒。

响应到达需要一段时间。这就是我想要异步沟通的原因。

I found this on msdn that explains how it's done.

// 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("http://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;
}

对于顶级概述,这里是我的Main方法的样子(它没有编译):

var pi1 = new RaspberryPi(@"http://192.168.0.100:8080");    // specify IP
var led = new Led(255, 100, 180);                           // r, g, b values wrapped in an Led object

Led result = await pi1.setLedAsync(2, led);      // FAIL    // what should be an async POST, awaiting the response

我希望这是有道理的。

Led类只是一个数据对象,为3个通道保存3 byte个s和json的一些转换方法。

setLedAsync方法:

async public Task<Led> setLedAsync(uint n, Led led)
{
    var client = new HttpClient();
    client.BaseAddress = _uri;

    var content = new StringContent(led.ToJson(), Encoding.UTF8, "application/json");

    Task<HttpResponseMessage> response = client.PutAsync("/led/" + n, content);
    HttpResponseMessage responseMessage = await response;

    string json = await responseMessage.Content.ReadAsStringAsync();

    return new Led(json);
}

错误

这一行是使用await时收到错误的地方:

Led result = await pi1.setLedAsync(2, led);

await只能用于async方法。

问题

  1. 为什么会出现此错误?示例代码中的最后一个注释行

      

    //等待AccessTheWebAsync的任何方法都会检索长度值。

    让我觉得应该这样做。据我了解,await基本上将Task<T>解开为T

    如果我不使用await,我会收到类型错配,因为该方法返回Task<Led>,而不是Led

  2. 让我感到困惑的是理解这个例子和我的情况之间的区别。我必须在await方法中使用async两次:

    • HttpResponseMessage responseMessage = await response;
    • string json = await responseMessage.Content.ReadAsStringAsync();

    问题在于我必须以HttpResponseMessage作为中间人。我怀疑我过早地放弃了第二次异步,等待某种方式(如果这有道理)我认为这是问题的根源,但我不知道如何解决它。

  3. 修改

    我将函数调用包装在一个asyn方法中,该方法允许编译代码。 但它不是异步的。我在服务器端添加了一个延迟来测试它。

    class Program
    {
        static void Main(string[] args)
        {
            var prog = new Program();
            Console.WriteLine("before init");
            prog.init();
            Console.WriteLine("after init");   // not happening before the response arrives
            Console.Read();
        }
    
        private async void init()
        {
            var pi1 = new RaspberryPi(@"http://192.168.0.100:8080");    // specify IP
            var led = new Led(255, 0, 0);                               // r, g, b values wrapped in an Led object
            Console.WriteLine("before await");
            Led result = await pi1.setLedAsync(2, led);                 // what should be an async POST, awaiting the response
            Console.WriteLine("after await");
        }
    }
    

    &#34;&#34;之后&#34;在请求的响应到来之前,消息将写入控制台。

1 个答案:

答案 0 :(得分:8)

您收到错误,因为异步等待 - 等待 - 意味着执行等待的方法本身是异步的。我想你不明白等待意味着什么。等待意味着同意阻止,直到结果可用 - 这就是相反的含义。等待意味着立即返回,以便我的呼叫者可以在不等待此结果的情况下完成重要工作;当结果可用时,在将来的某个时间安排此方法的其余部分。 Await是异步等待。当你等待信件到达邮件时,你不会坐在门边,什么都不做,直到它到来;你以异步方式完成其他工作,然后在信件到达后的某个时间恢复阅读邮件的任务。

你说方法 - 等待的方法,而不是等待返回任务的方法 - 是Main。如果这是一个控制台应用程序,那么你不能使Main异步,因为当Main返回程序结束时。再次,内化这个: await只是从当前方法的角度来看的回归。当前的方法将来会再次被称为 ,并将从中断的地方继续,但是当Main返回时,没有未来。所以你不能让Main async。

您注意到异步将T的任务转换为T,这是正确的,但它是异步。所以你在这里的主要选择是:

  • 从主
  • 将T的任务转换为T 同步
  • 编写除控制台应用以外的某种应用;比方说,一个winforms或WPF应用程序,在你告诉它
  • 之前不会终止