如何在同步代码中使用await / async?
我正在尝试使用await / async以使某些同步代码异步。 例如,这可以解除UI线程的阻塞:
private async void button1_Click(object sender, EventArgs e) { var task = DoRequestAsync(); textBox1.Text = "starting async task"; var text = await task; textBox1.Text = text; } private async Task DoRequestAsync() { try { var client = new HttpClient(); client.Timeout = new TimeSpan(0, 0, 0, 5); await client.GetAsync("http://123.123.123.123"); // force a timeout exception } catch (Exception e) { } return "done!"; }
但这不会,并将挂起UI线程:
private async void button1_Click(object sender, EventArgs e) { var task = DoRequestAsync(); textBox1.Text = "starting async task"; var text = await task; textBox1.Text = text; } private async Task DoRequestAsync() { try { var request = WebRequest.Create("http://123.123.123.123"); request.Timeout = 5000; request.GetResponse(); // force a timeout exception } catch (Exception e) { } return "done!"; }
我试图理解为什么会这样。 我的印象是var task = DoRequestAsync()
将创建一个新线程并异步运行方法中的所有内容,但似乎并非如此。
我可以用它来使它工作:
await Task.Run(() => { var request = WebRequest.Create("http://123.123.123.123"); request.Timeout = 5000; request.GetResponse(); });
但这看起来有点笨拙,我不确定这是否是解决这个问题的正确方法。 有谁知道如何使用Tasks和async / await以异步方式运行一堆同步代码?
这是正确的解决方案。 WebRequest.GetResponse
不是异步方法,因此它不返回任务。 它无法异步运行。
实际上,你所拥有的是你能得到的最正确和最Task.Run
解决方案(使用Task.Run
)。
我的印象是var task = DoRequestAsync()将创建一个新线程并异步运行方法中的所有内容,但似乎并非如此。
这不是魔术。 为了使它以异步方式运行,必须在异步方法中创建一个新的Task(而不是线程),或者它必须等待一个或多个方法)返回Task
, Task
或void
(这是用于事件处理程序) 。
你在方法中的最后一个语句return "done!";
只返回一个结果为“done”的已完成的Task
。
作为旁注,这就是HttpClient成为事实上的类HTTP请求的原因,特别是对于与Web API的互操作,而且对于通用的GET / POST /等: 它具有异步支持 。
任务还支持包装Begin * / End *函数(符合以前的异步编程模型–APM )。 你也可以这样做:
try { var request = WebRequest.Create("http://123.123.123.123"); request.Timeout = 5000; await Task.Factory.FromAsync(request.BeginGetResponse(), request.EndGetResponse, null); // force a timeout exception } catch (Exception e) { //TODO handle exception here }
这是完全正确的解决方案。
如果要使用异步,则必须确保始终保持异步。 换句话说,如果您没有调用方法的异步版本,则需要自己使用Task包装它。