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

nul*_*ull 0 .net c# asynchronous async-await

情况

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

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

我在msdn上发现了这个问题,解释了它是如何完成的.

// 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;
}
Run Code Online (Sandbox Code Playgroud)

对于顶级概述,这是我的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
Run Code Online (Sandbox Code Playgroud)

我希望这是有道理的.

Led类只是保持3个中的数据对象byteS为3个信道和一些转换方法和从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);
}
Run Code Online (Sandbox Code Playgroud)

错误

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

Led result = await pi1.setLedAsync(2, led);
Run Code Online (Sandbox Code Playgroud)

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中间人的身份对待这个问题.我怀疑我过早地放弃了异步性,等待某种方式等待(如果这有任何意义)我认为这是问题的根源,但我不确定如何解决它.

编辑

我将函数调用包装在一个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");
    }
}
Run Code Online (Sandbox Code Playgroud)

在请求的响应到来之前,没有"after"消息写入控制台.

Eri*_*ert 8

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

你说方法 - 等待的方法,而不是等待返回任务的方法 - 是Main.如果这是一个控制台应用程序,那么你不能使Main异步,因为当Main返回程序结束时.再次,内化这个:await只是从当前方法的角度来看.当前的方法将在稍后的时间再次调用,并将在它停止的地方继续调用,但是当Main返回时,没有未来.所以你不能使主要异步.

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

  • 从Main 同步将T的任务转换为T.
  • 写一些除了Console应用程序以外的应用程序; 比方说,winforms或WPF应用程序,直到你告诉它才会终止