【发布时间】:2015-09-24 22:44:32
【问题描述】:
情况
我将树莓派设置为服务器,通过 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 个 bytes 以及一些与 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 方法中使用。
问题
-
为什么会出现此错误?示例代码中的最后一行注释
// 等待 AccessTheWebAsync 的任何方法都会检索长度值。
让我觉得应该这样做。据我了解,
await基本上将Task<T>解包为T。如果我不使用
await,我会得到类型不匹配,因为该方法返回Task<Led>,而不是Led。 -
让我感到困惑的是理解示例和我的情况之间的区别。我必须在我的
async方法中使用两次await:HttpResponseMessage responseMessage = await response;string json = await responseMessage.Content.ReadAsStringAsync();
问题是我必须以中间人的身份处理这个
HttpResponseMessage。我怀疑我过早地放弃了第二次等待的异步性(如果这有任何意义的话)我认为这是问题的根源,但我不知道如何解决它。
编辑
我将函数调用包装在一个允许编译代码的异步方法中。 但它不是异步的。我在服务器端添加了一个延迟来测试这个。
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");
}
}
在请求的响应到达之前,不会将任何“之后”消息写入控制台。
【问题讨论】:
-
错误很明显:"
await只能在async方法中使用"。这与您正在等待的方法无关,而与您正在调用它的方法有关来自。 -
@sstan 这就是我尝试删除它的原因,但没有成功。我还解释了官方示例代码如何建议应该使用
await,或者至少我认为是这样。随意在答案中详细说明。 -
这是一个控制台应用程序吗?您不能在控制台应用程序的 Main 中异步等待,因为程序将在异步等待返回时结束。当异步数据可用时,不再有程序可以恢复。
-
我认为您的根本问题是您试图通过查看 Internet 上的示例来了解异步编程的工作原理,然后在不了解其作用的情况下编写代码。这种探索性学习通常是一种很好的技术,但如果您不准确地了解各种语义的含义,异步编程可能真的复杂且难以理解操作是关于控制流的。我会先仔细阅读一些关于 await 工作原理和用途的初学者文章。
-
如果您不想使用 await,没有什么能阻止您注册回调;这就是 await 所做的,就是为你注册一个回调。人们告诉我们,编写注册回调的程序太难了。例如,考虑如何注册一个回调,该回调将回调到包含异常处理的嵌套循环的中间。这在 JS 或 C# 中很难做到,没有人能做到,但用 await 很容易做到。
标签: c# .net asynchronous async-await