9

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?

Klemenko
  • 704
  • 1
  • 10
  • 21
  • You can check how SqlCommands' ExecuteAsync method is implemented here : https://referencesource.microsoft.com/#System.Data/System/Data/SqlClient/SqlCommand.cs – Dimitar Feb 05 '18 at 14:23
  • [Related question](https://stackoverflow.com/questions/33027364/how-are-asynchronous-i-o-methods-processed) – YuvShap Feb 05 '18 at 14:28

3 Answers3

10

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.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • 1
    Great answer, but now let's assume I am an owner of framework and I want to provide some async functionality. How? – Klemenko Feb 05 '18 at 14:51
  • @Klemenko That's going to be entirely dependant on the operation you're doing. In the case of IO, the operations are *inherently* asynchronous, so they're *already* asynchronous unless you specifically go out of your way to block the current thread until they complete. – Servy Feb 05 '18 at 14:53
  • I think that I understand your point, but to be sure: what if I have to call some WCF synchronous methods: as a client, do I have no other choice than to block the thread waiting for a request to be finished? That's pretty inefficient. – Boiethios Dec 05 '19 at 14:10
  • 1
    @FrenchBoiethios That's a situation where the underlying operation is inherently asynchronous, but the library you're using that is performing the operation for you synchronously blocks until the asynchronous operation has finished, and might not expose any way of doing the work in question asynchronously. That's not because the operation can't be done asynchronously, it's just that the choice has been taken out of your hands. – Servy Dec 06 '19 at 03:48
  • @Servy I understand. Thank you for taking the time to anwser. – Boiethios Dec 06 '19 at 08:11
1

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;
Crowcoder
  • 11,250
  • 3
  • 36
  • 45
  • 3
    I think the OP wants to know how to create asynchronous IO not how to consume it -> this does not really answer the question IMO – Christoph Fink Feb 05 '18 at 14:31
  • @ChrFin Indeed, I want how to create them without using Task.Run() method – Klemenko Feb 05 '18 at 14:32
  • Thanks for editing your answer. I quick checked the link inside about creating awaitables like transform Action or Func to Task and await them and it looks the exact thing I was looking for. – Klemenko Feb 05 '18 at 19:31
  • Hi @Klemenko, I am also having same doubt like how we want to create our own I/O bound async await method. Please can you share your idea on how to implement it. I have referred the link, but couldn't understand it completely – Raja Kondla Sep 18 '19 at 17:28
0

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.

Christopher
  • 9,634
  • 2
  • 17
  • 31
  • 1
    I never said they did not exist. I said: "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." I pointed out taht while it existed, it was usually just several orders of magnitude easier to use Multithreading to make Multitasking. – Christopher Feb 05 '18 at 14:38
  • Oh, so you never said, "it was the only way of Multitasking we had" Additionally, you refer to BGW as an older way of performing parallelism without threads, and yet BGW *performs its parallelism using threads* ,so that's not even a proper example of how to do work in parallel without multithreading. – Servy Feb 05 '18 at 14:40
  • I changed it to "And for a long time, it was effectively the only way of Multitasking we could use." Happy now? – Christopher Feb 05 '18 at 14:43
  • No, because it's still wrong, and also because you haven't actually answered the question. Making a bunch of factually incorrect statements about the history of asynchrony in C# isn't answering the question of how to avoid multithreading when doing this particular asynchronous operation. – Servy Feb 05 '18 at 14:44
  • @Servy: So you are generally pissed, there is nothing I can do to make you happy. Good to know. Means I can stop caring about what you say :) – Christopher Feb 05 '18 at 21:03
  • Of course you can make me happy. You can actually have a factually correct answer that answers the question, instead of a factually incorrect answer that doesn't answer the question. That you aren't interested in having a correct answer to the question is unfortunate. – Servy Feb 05 '18 at 21:08
  • So it was easy or even common to make Multitaskign without Multithreading? Then show me the code. – Christopher Feb 05 '18 at 21:30
  • To do what, specifically? – Servy Feb 05 '18 at 21:33
  • To prove that I am wrong. You know, the point of you starting this dicussion with me? Or is there nothing in the direction because I was not wrong? – Christopher Feb 05 '18 at 21:35
  • No, I mean, what do you specifically want a demonstration of how to do asynchronously? There are all sorts of things that one can do, and how you'd do them asynchronously would depend on what it is. I wasn't asking why I'd bother to provide you an example; I'm more than happy to provide an example. – Servy Feb 05 '18 at 21:41
  • "No, I mean, what do you specifically want a demonstration of how to do asynchronously?" That these sentences were wrong: "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." You know, just about the **point** of this argument? – Christopher Feb 05 '18 at 21:45
  • Like I said, I'm more than happy to give you a demonstration of how to do a given thing asynchronously using C# 4.0, but *you need to tell me what you want an example of*. Do you just want an example of *anything* that's asynchronous? – Servy Feb 05 '18 at 21:48