异步等待阻止 ui wp8 [英] async await blocking ui wp8
问题描述
据我所知 async await 关键字是这样工作的:当编译器遇到 await 关键字时,它会将异步函数的执行挂起到调用者(在这种情况下是调度程序),等待可等待函数在后台完成,然后返回函数.我说得对吗?
As far as I know the async await keywords work this way: when an await keyword is encountered by the compiler, it suspends the execution of the async function to the caller (in this case the dispatcher), waits for the awaitable function to complete in the background and then then returns back to the function. Am I right?
这是我在做什么:
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 会卡住?
This blocks the UI thread. why is this happening shouldn't the control be transferred back to the dispatcher when it encounters the await keyword? If so why does the UI get stuck?
推荐答案
您对 await
的理解大部分是正确的,只是不完整.await
不会立即暂停当前方法的执行.首先,要等待的值需要解析为一个值.通常,您将使用某种方法来创建 Task
.该任务将被同步创建,然后在创建该任务后,当前方法的执行将结束,并且将继续执行该方法的其余部分的任务.
Your understanding of await
is mostly correct, it's just incomplete. await
doesn't suspend execution of the current method immediately. First, the value to be awaited needs to be resolved to a value. Typically you'll have some sort of method that creates a Task
. That task will be created synchronously, and then after creating that task, execution of the current method will end, and a continuation will be applied to that task that executes the remainder of the method.
对于 async
方法,第一个 await
之前的所有内容都将由调用者同步执行.当第一个 await
(或方法结束,或方法未捕获的异常)导致方法返回实际的 Task
时.
For an async
method everything before the first await
will be performed synchronously by the caller. It is when the first await
(or the end of the method, or an exception that isn't caught by the method) that causes the method to return an actual Task
.
你的query
方法应该能很快创建代表操作完成的Task
然后返回,这样调用者可以await
那个Task
.但事实并非如此,它花了超过 5 秒的时间来创建表示何时完成的任务,以及当它最终将该任务交给 Task
已经完成的调用者时.
Your query
method should be able to create the Task
that represents the completion of the operation very quickly and then return, so that the caller can await
that Task
. But it doesn't, it spends over 5 seconds creating the task that represents when it will finish, and when it ends up giving that task to its caller that Task
has already finished.
这意味着 Button_Tap
在 query
完成其 5任务创建时间.
This means that Button_Tap
can't yield to its caller (which is the UI's message loop) until after query
has finished its 5 seconds of task creation.
所有这一切意味着 Button_Tap
是异步的 query
需要是异步的,而不是异步的,因为它使用了 async
关键字,但在某种意义上是异步的,因为该方法在被调用时几乎立即返回,并且在将控制权交还给调用者之后执行其工作.
All of this means that for Button_Tap
to be asynchronous query
needs to be asynchronous, and not asynchronous in that it uses the async
keyword, but asynchronous in the sense that the method returns almost immediately when called, and that it performs its work after yielding control back to the caller.
至于如何使方法异步,这将取决于.对于 IO,例如查询数据库,您真正想要的是让查询提供程序本身提供固有的异步支持.您希望有一个查询方法返回一个 Task
,您可以await
,而不是同步返回其结果.如果您别无选择,那么作为最后的手段,您可以使用 Task.Run
在非 UI 线程中执行查询,然后 await
那个.
As to how to make the method asynchronous, that'll depend. For IO, such as querying a database, what you really want is to have the query provider itself provide inherently asynchronous support. You want to have a query method that returns a Task
that you can await
, rather than synchronously returning its results. If you have no other choice, then as a last resort you can use Task.Run
to perform the query in a non-UI thread and then await
that.
至于Thread.Sleep
,也就是同步阻塞线程.您需要在一段时间后异步执行某些操作(如果您确实需要延迟).您可以为此使用 Task.Delay
.
As for the Thread.Sleep
, that is also synchronously blocking the thread. You need to asynchronously do something after a set period of time instead (if you really do need the delay). You can use Task.Delay
for that.
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;
}
顺便说一句,您真的不应该像这样尝试重用您的数据库连接.对于初学者来说,它们不是为了同时使用而设计的.实际上,它们本质上是为执行单一操作而设计的.您应该在需要时立即创建连接,并在执行该操作后立即清理它.
On a side note, you really shouldn't be trying to re-use your database connections like this. They aren't designed to be used concurrently, for starters. Really they're inherently designed to perform just a single operation. You should be creating your connection right when you need it, and cleaning it up as soon as that one operation has been performed.
这篇关于异步等待阻止 ui wp8的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!