异步等待阻塞UI WP8

6
据我所知,async await关键字的工作原理是这样的:当编译器遇到await关键字时,它会暂停异步函数的执行并将其返回给调用者(在本例中为dispatcher),等待可等待函数在后台完成,然后再返回到该函数。我说得对吗?
以下是我正在做的事情:
async private void Button_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
     var p = await query("select* from notes", con);
}   

这里是函数被调用的地方。

 async Task<List<notes>> query(string sqlstring, SQLiteConnection con)
 {
       var p = con.Query<notes>(sqlstring);
       Thread.Sleep(5000);
       MessageBox.Show(p[0].Data); 
       return p;
 }

这会阻塞UI线程。为什么会这样?当遇到await关键字时,控制不应该自动传递回分发程序吗?如果是这样的话,为什么UI会卡住呢?


1
请修改您的帖子,将“I”和所有句子的首字母大写。谢谢。 - Robert Harvey
1个回答

13

您对await的理解 大部分 是正确的,只是不完整。 await 不会立即挂起当前方法的执行。首先,需要将要等待的值解析为一个值。通常情况下,您会有某种创建 Task 的方法。该任务将同步创建,然后在创建该任务之后,当前方法的执行将结束,并将一个延续应用于该任务,以执行余下的方法。

对于async 方法,在第一个 await (或方法结束,或方法未捕获的异常)之前,调用方将按同步方式执行。当出现第一个await时,方法将返回实际的 Task

您的query方法应该能够快速创建表示操作完成的Task并返回,以便调用者可以 awaitTask。但是它没有这样做,它花费了5秒钟以上来创建代表其完成时间的任务,而当它将该任务交给其调用者时,该 Task 已经完成了。

这意味着Button_Tap不能向它的调用者(即UI的消息循环)让步,直到 query 完成了其5秒钟的任务创建。

所有这些意味着为使Button_Tap异步,query需要是异步的,并且不是使用async关键字,而是指当调用时立即返回该方法,然后在将控制权交回给调用者后执行其工作。

至于如何使该方法异步,那就要视情况而定。对于IO(例如查询数据库),您真正想要的是查询提供程序本身提供内在的异步支持。您想要一个返回Task的查询方法,以便您可以await它,而不是同步返回结果。如果没有其他选择,那么最后的选择是可以使用Task.Run在非UI线程中执行查询,然后await该查询。

至于 Thread.Sleep,那也会同步阻塞线程。您需要异步地在一段时间后执行某些操作(如果您确实需要延迟)。您可以使用Task.Delay来实现。

async Task<List<notes>> query(string sqlstring, SQLiteConnection con)
{
    var p = await con.QueryAsync<notes>(sqlstring);
    //Or, if there is no asynchronous query method
    //var p = await Task.Run(() => con.Query<notes>(sqlstring));
    await Task.Delay(5000);
    MessageBox.Show(p[0].Data);
    return p;
}

另外,你真的不应该这样尝试重新使用数据库连接。首先,它们不是为并发使用而设计的。实际上,它们本质上只设计用于执行单个操作。你应该在需要连接时创建它,完成一次操作后立即清理。


+1 非常清晰和精彩的解释。感谢数据库连接提示 :) - B0rn2C0de
这条评论对我来说也很有意义。当你说“你真正想要的是查询提供程序本身提供固有的异步支持”,他们可能已经使用实际的线程来实现了,对吗? - user1610325
@user1610325 不,当查询提供程序为数据库查询提供适当的异步支持时,它们根本不使用任何线程。 - Servy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接