Klemenko
Klemenko

Reputation: 724

C# async/await for I/O-Bound vs CPU-Bound operation

I am learning about asynchronous programming in C#. In this article I found that for IO-Bound operations you should not use Task.Run() but I don't know how to create a task without Task.Run()... For example I want to create a custom method named GetPageCountAsync, which query database and return results. I have method GetPageCount already which query database synchronous. I don't know better way than:

private async Task<int> GetPageCountAsync()
{
  return await Task.Run(() => GetPageCount());
}

How to do this without Task.Run? I found a ExecuteReaderAsync method of SqlCommand class but I wonder how is this method implemented? Without Task.Run? If so, how?

Upvotes: 11

Views: 10005

Answers (3)

Crowcoder
Crowcoder

Reputation: 11514

To make your method async all you have to do is await an asynchronous operation within it, and make the method signature like this (Assuming the return value here...):

async Task<int> GetPageCountAsync()
{
    //not shown is how to set up your connection and command
    //nor disposing resources
    //nor validating the scalar value
    var pages = await command.ExecuteScalarAsync().ConfigureAwait(false);
    return (int)pages;
}

If you have no async methods to call in the libraries you are using you can create your own awaitables but it should be extremely rare you need to go to such lengths.

Now that GetPageCountAsync is async you simply await it:

return await GetPageCountAsync();

It is also good practice for non-context aware code such as this to allow the code to resume on another context like:

return await GetPageCountAsync().ConfigureAwait(false);

If you have other work to do, not dependent on the result of the query, you can start it and await it later to perform work in parallel:

 Task pageCountTask = GetPageCountAsync();
//do something not dependent on page count....
return await pageCountTask;

Upvotes: 1

Christopher
Christopher

Reputation: 9804

There are two terms that can be mixed up easily: Multitasking and Multithreading.

Multithreading is a form of Multitasking. And for a long time, it was effectively the only way of Multitasking we could use. While we could do Multitasking without threads, that was often cumbersome to write. It was easier to write and use the BackgroundWorker in an GUI environment, then writing proper multitasking.

But threads have issues: the need to Invoke. That they love to swallow exceptions. And not to forget, we actually need a Event Queue to even do callbacks.

Async & await were added with C# 5.0 I think. The goal seems to have been to allow Multitasking without Multithreading or excessive need to write code. The compiler and runtime will do all that pesky switching between contexts within the same thread for you. You could still use Threads for this. You have to use them for CPU bound operations. But for many cases using Multitasking is simpler and better.

Upvotes: 0

Servy
Servy

Reputation: 203829

It is the responsibility of whatever framework you're using to perform the IO operation to provide an inherently asynchronous operation to you. It can either provide a method returning a Task, or some other asynchronous operation (i.e. a method that takes a callback, returns an IAsyncResult, fires an event, etc.) that can be turned into a Task.

If the operation you're calling already synchronously blocks the thread until the asynchronous result has completed, then it's already too late. Most DB frameworks are good about providing some form of asynchronous way of querying the database, so odds are another method is out there for you to use. If you're using a framework that doesn't provide any asynchronous version of the method, then there's no way for you to avoid using multiple threads; the author(s) of the framework have taken that choice out of your hands.

Upvotes: 10

Related Questions