尝试从使用BackgroundWorker升级到Task,我对如何在不调用Wait()的情况下保持后台作业运行感到困惑。
这是后台工作(简化):
private async Task RunAsync()
{
HttpResponseMessage response = await TheHttpClient.GetAsync(Path);
if (response.IsSuccessStatusCode)
{
textBox_Response.Text = await response.Content.ReadAsStringAsync();
}
}
单击“发送”按钮时,上面的代码应该运行,但由于后台代码有多个await
异步调用,我猜需要进行多次Wait()
次调用:
private void button_Send_Click(object sender, EventArgs e)
{
Task t = RunAsync();
while (!t.IsCompleted)
{
t.Wait();
}
}
但是这会在后台作业的整个处理时间内阻止GUI。
如何启动后台作业并立即从按钮单击处理程序返回,同时允许后台作业运行所有异步调用?
我知道如何使用BackgroundWorker实现这一点,我不应该在这里使用任务吗?
答案 0 :(得分:6)
事件处理程序是您唯一允许执行<Connector protocol="org.apache.coyote.http11.Http11Protocol"
port="8080" redirectPort="8443"
<Connector protocol="org.apache.coyote.http11.Http11Protocol"
port="8443"
SSLEnabled="true"
scheme="https"
secure="true"
sslProtocol="TLS"
keystorePass="<pass>" keystoreFile="<path to keystore>"
ciphers="TLS_RSA_WITH_AES_256_CBC_SHA, <add more ciphers>"/>
的地方,您所做的是使事件处理程序异步并等待它
async void
答案 1 :(得分:1)
人们常常认为async await是由多个线程执行的异步进程,但实际上它是由一个线程完成的,除非你使用Task.Run或类似函数启动一个新线程。
在this interview(中间的某个位置。搜索异步)Eric Lippert比较了餐厅厨师的作品的异步等待。如果他正在烤面包,他可以在煮鸡蛋之前等待面包准备好,或者他可以开始煮鸡蛋然后再回到面包里。看起来厨师当时做了两件事,但实际上他只做了一件事,每当他不得不等待什么时,他就开始环顾四周,看看他是否可以做其他事情。
如果您的唯一线程调用异步函数,而不等待它。你的唯一线程开始执行该函数,直到他看到等待。如果他看到一个,他就不会等到异步功能完成,而是记得他在等待的地方,然后在他的调用堆栈中查看他的调用者是否还有别的事情要做(==不等待)。调用者可以进行下一个语句,直到他遇到等待。在那种情况下,控制被给予调用者回调给调用者,直到他等待等等。如果你的唯一线程正在等待调用栈控制中的所有函数,则返回到第一个await。完成后,你的线程在等待之后开始执行语句,直到他遇到另一个await,或者直到函数完成。
您可以确定每个异步函数都在等待某个地方。如果没有等待,则将其创建为异步函数没有意义。事实上,如果您忘记等待异步函数中的某个地方,您的编译器会发出警告。
在您的示例中,您可能会遇到错误,或者至少是您忘记声明button_send_click异步的警告。如果没有这个,程序就等不及了。
但是在你宣布它是异步的并且按下按钮之后,你的GUI线程将调用RunAsync,它将进入函数TheHttpClient.GetAsync。
在此函数内部等待ReadAsStringAsync,并且因为ReadAsStringAsync被声明为异步,我们可以确定该函数有一个等待。只要满足此等待时间,就会将控制权返回给您的TheHttpClient.GetAsync。如果没有等待,该函数可以自由执行下一个语句。一个例子是这样的:
private async Task RunAsync()
{
var responseTask = TheHttpClient.GetAsync(Path);
// because no await, your thread will do the following as soon as GetAsync encounters await:
DoSomethingUseFul();
// once your procedure has nothing useful to do anymore
// or it needs the result of the responseTask it can await:
var response = await responseTask;
if (response.IsSuccessStatusCode)
...
await在DoSomethingUseful()之后。因此,该功能无法继续。在responseTask完成之前,不会执行任何操作,而是将控制权返回给调用者:button_send_click。如果该功能没有等待它可以自由地做其他事情。但是,因为等待控制的button_send_click被返回给调用者:你的UI可以自由地做其他事情。
但请记住:永远是独一无二的厨师做早餐你的线程一次只做一件事
优势在于您不会遇到多线程遇到的困难。缺点是,只要你的线程没有满足等待,它就太忙了,无法做其他事情。
如果您不想在忙着进行计算时需要进行一些冗长的计算,可以启动一个单独的线程来进行此计算。这使您的线程有时间做其他事情,比如保持UI响应,直到它需要计算结果:
private async void Button1_Clicked(object sender, ...)
{
var taskLengthyCalculation = Task.Run( () => DoLengthyCalculation(...));
// while a different thread is doing calculations
// I am free to do other things:
var taskRunAsync = RunAsync();
// still no await, when my thread has to await in RunAsync,
// the next statement:
DoSomethingUseful();
var result = await taskRunAsync();
var resultLengthyCalculation = await taskLengthyCalculation;