I'm currently developing a small server application and getting to grips with Task<>, and other associated operations.
I'm wondering how Blocking operations work within a Task.
So for example, I currently use several Libraries with "blocking" operations. One of them is Npgsql (PostgreSQL provider.)
If I do the following...
Task myTask = new Task<>( () =>
{
using(var db = new PostgresqlDatabaseConnection())
{
db.ExecuteQuery("SELECT takes 50 ms to get data...")
db.Insert(anObject); etc....
}
}
).Start();
And say, chain it to a bunch of other tasks that process that data.
Is this Efficient? I.E. Let's say that ExexuteQuery calls some kind of Thread.Sleep(1) or somehow blocks the thread, is this going to effect my Task Execution?
I'm asking because my server uses several libraries that would have to be rewritten to accomodate a totally Asynchronous methodology. Or is this Async enough?
*My Thoughts *
I'm really just not sure.
I know that if for example, the db.Executre() just ran a while(true) loop until it got it's data, it would almost certainly be blocking my server. Because a lot of time would be spend processing while(true). Or is Task smart enough to know that it should spend less time on this? Or if internally it is using some waiting mechanism, does the Task library know? Does it know that it should be processing some other task while it waits.
I'm currently developing a small server application and getting to grips with Task<>, and other associated operations.
You won't benefit from using new Task
, Task.Factory.StartNew
, Task.Run
in the server-side application, unless the number of concurrent client connections is really low. Check this and this for some more details.
You would however greatly benefit from using naturally asynchronous API. They don't block a pool thread while "in-flight", so the thread is returned to the pool and then can get busy serving another client request. This improves your server app scalability.
I'm not sure if PostgreSQL provides such API, look for something like ExecuteQueryAsync
or BeginExecuteQuery
/EndExecuteQuery
. If it doesn't have that, just use the synchronous ExecuteQuery
method, but do not offload it to a pool thread as you do in your code fragment.
Using the async/await features of C# 5 will definitely make things easier. It can make asynchronous code easier to write, as you write it very similar to how you would write synchronous code.
Take the following example. I am using Thread.Sleep to simulate a long running operation, so any libraries that don't support async natively can still be used via Task.Run
. While the Thread.Sleep
is holding up the thread, your UI is still responsive. If you had written this code synchronously, your UI would hold up for 1.5 seconds until the thread.sleep
was finished.
private async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("0");
await DoWorkAsync();
Console.WriteLine("3");
}
private async Task DoWorkAsync()
{
Console.WriteLine("1");
await Task.Run(()=>
{
// Do your db work here.
Thread.Sleep(1500);
});
Console.WriteLine("2");
}
So in short, if you have a long running database operations and you want to keep your UI responsive, you should leverage async/await. While this does keep your UI responsive, it does introduce new challenges like: what happens if the user clicks a button multiple times
or what if the user closes the window while you are still processing
to name some simple cases.
I encourage you to read further on the subject. Jon Skeet has a multi-part series on async. There are also numerous MSDN articles on the subject: 1 2 3 ...
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