简体   繁体   English

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

[英]Why can I not await the return value from my async method?

Situation 情况

I have a raspberry pi set up as a server taking json via HTTP as input. 我有一个树莓派设置为服务器,通过HTTP将json作为输入。 The "API" allows to set leds connected to the pi. “ API”允许设置连接到pi的led。 That all works, I can send requests from the browser and everything is great. 一切正常,我可以从浏览器发送请求,一切都很好。

It takes a while for the response to arrive. 回复需要一段时间。 That's why I want to communicate asynchrounously. 这就是为什么我想随意地交流。

I found this on msdn that explains how it's done. 我在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; } 

For the top level overview, here's how my Main method looks like (it doesn't compile): 对于顶级概述,这是我的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

I hope that makes sense. 我希望这是有道理的。

The Led class is just a data object holding 3 byte s for the 3 channels and some conversion methods to and from json. Led类只是一个数据对象,它为3个通道保留3 byte ,并提供往返于json的一些转换方法。

The setLedAsync method: 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);
}

Error 错误

This line is where I get an error for using await : 这行是我在使用await出错的地方:

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

await can only be used in an async method. await只能在async方法中使用。

Questions 问题

  1. Why do I get this error? 为什么会出现此错误? The last comment line in the example code 示例代码中的最后一条注释行

    // Any methods that are awaiting AccessTheWebAsync retrieve the length value. //任何正在等待AccessTheWebAsync的方法都将检索长度值。

    makes me think that this is how it should be done. 让我认为这是应该做的。 As I understand it, the await basically unwrapps the Task<T> into a T . 据我了解, await基本上将Task<T>T

    If I do not use await , I get a type missmatch, because the method returns Task<Led> , not Led . 如果不使用await ,则会得到类型不匹配的类型,因为该方法返回Task<Led> ,而不是Led

  2. What's confusing for me is to understand the difference between the example and my situation. 对我来说,令人困惑的是,要理解示例与我的处境之间的区别。 I have to use await twice in my async method: 我必须在async方法中使用await两次:

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

    The thing is that I have to deal with this HttpResponseMessage as a middleman. 问题是我必须以中间人身份处理此HttpResponseMessage I suspect that I'm prematurely giving up the asynchronousity with this second await somehow (if that makes any sense) I think this is the origin of the problem, but I'm not sure how to solve it. 我怀疑我以某种方式(如果有任何意义)过早地放弃了第二次等待的异步性,我认为这是问题的根源,但我不确定如何解决。

Edit 编辑

I wrapped the function call in an asyn method, which allows to compile the code. 我将函数调用包装在一个asyn方法中,该方法可以编译代码。 But it's not asynchronous. 但这不是异步的。 I added a delay on the server side to test this. 我在服务器端添加了延迟以进行测试。

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");
    }
}

None of the "after" messages are written to the console before the response from the request arrives. 在来自请求的响应到达之前,没有“之后”消息被写入控制台。

You get the error because an asynchronous wait -- an await -- implies that the method doing the awaiting is itself asynchronous. 您会收到此错误,因为异步等待-等待-意味着进行等待的方法本身是异步的。 I think you do not understand what await means. 我认为您不明白等待的含义。 Await does not mean synchronously block until the result is available -- that is the opposite of what it means. 等待并不意味着同步阻塞,直到结果可用 -这是什么意思相反 Await means return immediately so my caller can do important work without waiting for this result; 等待意味着立即返回,这样我的呼叫者就可以做重要的工作而不必等待这个结果。 schedule the remainder of this method at some time in the future when the result is available . 当结果可用时,在将来的某个时间安排此方法的其余部分 Await is an asynchronous wait . 等待是异步等待 When you await a letter to arrive in the mail you do not sit by the door doing nothing until it arrives; 当您等待一封信件到达邮件时,请不要坐在门前什么也不做,直到它到达为止。 you do other work asynchronously and then resume the task of reading your mail at some time after the letter arrives. 您可以异步执行其他工作,然后在信件到达后的某个时间恢复阅读邮件的任务。

You say that the method -- the method doing the awaiting, not the method returning the task being awaited -- is Main. 您说方法-执行等待的方法,而不是返回正在等待的任务的方法-是Main。 If this is a console app then you cannot make Main asynchronous because when Main returns the program ends . 如果这是一个控制台应用程序,则不能使Main异步,因为Main返回时程序结束 Again, internalize this: an await is just a return from the perspective of the current method . 再一次将其内部化: 从当前方法的角度来看,等待只是返回 The current method will be called again later in the future and will pick up where it left off, but when Main returns there is no future. 当前方法将在以后稍后再次调用,并从中断处开始取回,但是当Main返回时,就没有未来。 So you cannot make Main async. 因此,您无法使Main异步。

You note that async turns a task of T into a T, which is correct, but it does so asynchronously . 您注意到异步将T的任务转换为T,这是正确的,但是它异步地完成了 So your principal choices here are here are: 因此,这里的主要选择是:

  • turn the task of T into a T synchronously from Main 从Main 同步将T的任务转换为T
  • write some sort of app other than a Console app; 编写除控制台应用程序以外的某种应用程序; say, a winforms or WPF app, which does not terminate until you tell it to 例如,winforms或WPF应用程序,直到您告诉它,它才会终止

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM