3

I've been learning about HttpClient (consuming API's in .NET in general), and therefore about async programming. I'm still pretty lost right now, so trying to clear some things up. One question I can't seem to find an answer to - are asynchronous methods implemented exclusively using the async keyword?

I understand that you could (theoretically) create synchronous methods using async programming, but how do you recognize that? For example, from this link and this example:

public string GetReleases(string url)
{
    using (var httpClient = new HttpClient())
    {
        httpClient.DefaultRequestHeaders.Add(RequestConstants.UserAgent, RequestConstants.UserAgentValue);

        var response = httpClient.GetStringAsync(new Uri(url)).Result;

        return response;
    }
}

The author says:

For the simplicity’s sake, I implemented it synchronously

But how do I recognize it is synchronous? Is it solely because the method is not defined with async Task, for example:

public async Task<string> GetReleases(string url)

Does that mean that in this HttpClient tutorial, this example is also not asynchronous:

// GET: Student
    public ActionResult Index()
    {
        IEnumerable<StudentViewModel> students = null;

        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("http://localhost:64189/api/");
            //HTTP GET
            var responseTask = client.GetAsync("student");
            responseTask.Wait();

            var result = responseTask.Result;
            if (result.IsSuccessStatusCode)
            {
                var readTask = result.Content.ReadAsAsync<IList<StudentViewModel>>();
                readTask.Wait();

                students = readTask.Result;
            }
            else //web api sent error response 
            {
                //log response status here..

                students = Enumerable.Empty<StudentViewModel>();

                ModelState.AddModelError(string.Empty, "Server error. Please contact administrator.");
            }
        }
        return View(students);
    }

So to sum up, my questions are:

  1. Is async programming (in .NET), and async methods, implemented exclusively using async and void/Task/Task?
  2. If not, how else, and if so, how do I recognize "true" asynchronous methods compared to synchronous methods implemented using asychronous principles (like the example above?)
  3. Why then did the above examples use "sync through async", since from everything I've read everyone says to NEVER do that? Are they just bad examples, or for simplicity sake (even so, shouldn't "the correct way" > "simplicity"? Would it be ok to use this in situations where, e.g., I have very simple code and want to avoid threading/deadlock issues, since I'm not comfortable with async programming just yet?
vpDev
  • 33
  • 5
  • "For simplicity's sake" he implemented it wrong, in more than 1 aspect. It's a bad example (first snippet "GetReleases"). – Fildor Jan 18 '19 at 10:20
  • 1
    `are asynchronous methods implemented exclusively using the async keyword?` - no, the keyword `async` means "I may use `await` inside this method". In order to be awaitable, a method must return `Task`, whether or not it itself is marked as `async`. – GSerg Jan 18 '19 at 10:21
  • 1
    To add to GSerg: Neither of which will tell you 100% that the function will be executed asynchronously, though. – Fildor Jan 18 '19 at 10:23
  • 2
    I would suggest https://blog.stephencleary.com/2012/02/async-and-await.html as a starting point, and then follow links from there (specifically, don't miss https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html). – GSerg Jan 18 '19 at 10:27
  • async essentially is a keyword that implies that you want to use asynchronous programming. (aka, it's a keyword that refers to a pattern -ish) – Dennis Vanhout Jan 18 '19 at 10:27
  • Ok so if `async` is needed only for `await`, how do I know if a method is asynchronous or not? Basically my 2nd & 3rd questions – vpDev Jan 18 '19 at 10:49
  • @GSerg I've already read the links you've posted, I haven't understood all of it, but they are regarding await & async in general. My questions are related to but also outside of these two keywords – vpDev Jan 18 '19 at 10:52
  • @vpDev You can have a look at https://stackoverflow.com/q/20350397/11683, but then, why do you want to know the answer? It doesn't tell you if the method will not block in any way; it still may at certain stages even if it's truly async. – GSerg Jan 18 '19 at 10:55
  • @GSerg I think we have a misunderstanding somewhere. I'm not asking about whether a method has the async/await keywords, like the link you provided, or whether it will block. If I can make my questions clearer I will, just tell me which part you didn't understand? – vpDev Jan 18 '19 at 11:37

3 Answers3

4

how can I recognize truely async/sync methods?

You can't. Not really. You can spot methods that are potentially async, but little else can be learned without consulting documentation or the implementation of those methods.

So, you can examine a method's return type. If it's void, you don't know much. If it's Task, Task<T>, ValueTask<T> or any other awaitable type1, then the method may be asynchronous. But bear in mind, the method signature may be fixed because the type inherited the method from a base class or it's implementing an interface; So whilst the method has the potential to be async, the actual implementation of that method may be completely synchronous.

Or, the method may have the potential to be asynchronous but may have particular control flows which lead to it behaving synchronously2. These may be e.g. that if certain conditions are true, the method already has the answer, so it returns it straight away. Otherwise it goes off and does something async - as one example.

If the return type isn't awaitable and it's non-void, all you can actually reason about the method is that, at the point at which it returns, it's supplied some value for that return type3. There's no way to reason about any other work that may have been started by that method - only that if it's done something like that, it doesn't intend for you to be able to discover when that work has completed.


If you're looking at the implementation of a method and asking yourself "is this implementation async" then the important thing is to work out what this code makes happen after control is returned back to the caller.

When is control returned back to the caller?

  • When we hit a return
  • When we hit an await, maybe

When we hit an await, we only return control back to the caller4 if the awaitable that we're awaiting isn't complete yet. So we have to find out where that awaitable came from and, if it came from calling another method, we have to start again from scratch in considering what that method does.

If the method contains awaits then it's usually safest to say that it's potentially async - but bear in mind the above possibilities about already completed awaitables and early returns.

If it's not async/await, what else might it have done? Well, if it's working with Tasks, it may have created one or more of those tasks to represent it's ongoing work. It may have scheduled more code to run via ContinueWith.

If it's not working with Tasks directly, hasn't called something else that is potentially async, hasn't cause a new Thread to be created and isn't needlessly obfuscated, it's probably synchronous.


Would it be ok to use this in situations where, e.g., I have very simple code and want to avoid threading/deadlock issues, since I'm not comfortable with async programming just yet?

The sync over async patterns shown in the examples in your question are more prone to deadlocks than working with async/await. Why? Because they block the current thread waiting for other things to happen. But the current thread or resources that it has locked may be special - and the actual async tasks that it's invoked may need to gain access to that same thread/resource before they can complete. Classic deadlock.


1Awaitable is one of a number of places where C# uses "duck typing". If there's a method available (instance or extension) called GetAwaiter that returns a type with the right shape, it's awaitable. So despite the fact that you'll probably never see one, be aware that custom awaitables are possible in the language.

2"Hot path" optimizations come to mind.

3And out/ref parameters. Of course, if it has those it won't be an async method implemented via the async/await keywords but it may still have some asynchronous behaviour.

4If we've already yielded control back to the caller during an earlier await, we won't return control back to the caller at later awaits, but we'll be returning it to "something" that isn't our code.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
0

An asynchronous method in C# can return void, Task or Task<T>, where void should be avoided in general because it cannot be awaited.

As a convention asynchronous methods should be called DoSomethingAsync()

The async keyword, however, is an implementation detail and does not belong to the API. This keyword is only required if you use an await in the method body. But that need not be the case. You could simply delegate to another asynchronous method, without the need to mark the method as async and using an await.

So 'true' asynchronous methods should be recognizable by the Async suffix of the method name, however, you can't be sure the implementor actually uses naturally asynchronous operations or even runs synchronously some parts or the whole method.

In the example he made the method synchronously by putting .Result at the end of GetStringAsync

Creepin
  • 482
  • 4
  • 20
  • `by the Async prefix of the method name` - unless the author did not bother to follow that convention, or unless it is a legacy method the name for which was selected before introduction of `await` (yes, they exist in .NET). – GSerg Jan 18 '19 at 10:28
  • 4
    Suffix, since we're nit-picking – Matt Evans Jan 18 '19 at 10:52
0
  1. If a method returns a Task or Task<T> (with exception to void in case of event handler) then it can be awaited and hence it can be asynchronous. async keyword is only an indication that it may await somewhere for another awaitable. Based on the control flow, async may return without actually awaiting anything. Asynchronous programming is not a new thing, it has existed in many forms like callbacks, Invokes etc.

  2. Examples you have provided are not using async await pattern properly. Microsoft has provided naming convention (Async Suffix) and Task<T>, Task as types for async programming. So if you see some method returning Task<T> or Task and Method name has suffix "Async" then you can consider it asynchronous. Although suffix thing is not required by compiler, it helps in differentiating it from its synchronous counterpart. (Read vs ReadAsync)

  3. They are bad examples, those action should be marked as async and all the Async methods should be awaited for result. There can be an exception in some console program where main can't be async.

Please read Stephen Cleary blog on async await

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
Sarvesh Mishra
  • 2,014
  • 15
  • 30