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?
here is what I am doing:
async private void Button_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
var p = await query("select* from notes", con);
}
and here is the function being called
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;
}
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?
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.
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
.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With