2

Yesterday, I learned the basics about C# multitasking. While I technically seem to understand how it works, I just don't see why it's done like it is. Bearing in mind that I'm new on the topic, that's probably a sign that I have understood something wrong. For example, You could write:

public async Task SomeEventHandler()
{   if(foo) return await SomeMethod("a");
    else return await SomeMethod("b");
}

async Task<int> SomeMethod(string whatever)
{   string bar = await SomeInput();
    return bar + " " + whatever;
}

What I don't see here is that why are the async and await keywords required? Why can't one just write:

public void SomeEventHandler()
{   if(foo) return SomeMethod("a");
    else return SomeMethod("b");
}

int SomeMethod(string whatever)
{   string bar = SomeInput();
    return bar + " " + whatever;
}

With the input method implementation being something like:

public string SomeInput()
{   while(!InputObject.stuffIncoming)
    {    CurrentTheard.DoSomethingElse();
    }
    return InputObject.Next();
}

Can somebody explain why that would not work, or would work in undesirable way? EDIT: According to answers it seems to be that for some reason, methods doing awaiting cannot be called like regular ones or vice-versa. But what causes that? At least in D you can both call or spawn a theard for a same function.

dukc
  • 121
  • 1
  • 9
  • https://blogs.msdn.microsoft.com/ericlippert/2010/11/11/asynchrony-in-c-5-part-six-whither-async/ – Matthew Watson Jul 11 '17 at 08:12
  • Related: [How and When to use `async` and `await`](https://stackoverflow.com/questions/14455293/how-and-when-to-use-async-and-await) – Patrick Hofman Jul 11 '17 at 08:18
  • [MSDN](https://msdn.microsoft.com/library/hh191443(vs.110).aspx) has pretty good explanation of what are `async/await` behind. As for you question, just try it. Add `Sleep(10000)` into `SomeInput` and see what will happen with `async/await` and without. – Sinatr Jul 11 '17 at 08:27
  • Well, still didn't understand troughoutly but a bit better yes. Thanks for the help. – dukc Jul 11 '17 at 10:56
  • `await` is all about *freeing up the current thread*. Your suggested alternative is essentially a *nested message loop*. The problems with nested message loops are: 1) They cause unexpected reentrancy (this is the reason behind the common saying "DoEvents is evil"). 2) It's very difficult for a library to know what kind of loop to run. 3) They don't have a way to work when there is no message loop (e.g., a thread pool thread, or Console main thread). – Stephen Cleary Jul 11 '17 at 12:27

1 Answers1

0

The Idea behind async and await is to provide a simple way to implement a "parallel execution with callback" approach. Whenever the keyword await is used the execution of the next line of code is delayed until the current line is completed (just like in a normal execution) but the current Thread is not blocked. This means that behind the scenes the following is happening:

  1. You call a long running operation with await
  2. The long running operation is executed in another Thread
  3. The rest of your methods code is registered as a callback method
  4. the calling Thread goes on with execution after the method starting the long running operation
  5. the long running operation eventually finishes
  6. the callback method (from point 3) is executed

The benefit is, that your code will remain easy to read. You don't have to bother with Threads and callbacks

Romano Zumbé
  • 7,893
  • 4
  • 33
  • 55
  • It's worth explicitly pointing out that in order to attain this benefit the underlying operation has to actually be asynchronous - you can't just arbitrarily apply this to any operation (the question seems to imply that `await`/non-`await` can just be swapped out at will). – Ant P Jul 11 '17 at 08:34
  • So it uses a diferent theard for every awaited call? But what's the point here? Why it has to push work somewhere to the future for every method calling something that has to do so, instead of just the low-level i/o function doing so? – dukc Jul 11 '17 at 08:51
  • Ant P Yes that's just what I'm thinking, but why they can't? – dukc Jul 11 '17 at 08:53
  • The method you await needs to return a `Task` object. This task is executed in another thread to make full use of the systems resources (multithreading, multi core). If you need to check a list of URLs to see if they are all reachable, you can do that sequentially or parallel. If you have 1000 URLs and each one needs one second to be verified the whole operation will take 1000 seconds. If you parallelize it on four cores it will take only 250 seconds (plus overhead). – Romano Zumbé Jul 11 '17 at 09:02
  • 2
    @RomanoZumbé it's misleading to say that "long running operation is executed in another Thread", because often (with most of asynchronous IO for example) it is not executed in another thread. – Evk Jul 11 '17 at 09:05
  • @Evk Where else than in another `Thread` would the `Task` run? – Romano Zumbé Jul 11 '17 at 14:44
  • This article explains it quite good: https://blog.stephencleary.com/2013/11/there-is-no-thread.html – Evk Jul 11 '17 at 14:46
  • @Evk It may be true, that on a hardware level something different is happening (but in the article he talks only about "pure" operations, like triggering an action in a device driver) but from the applications perspective every `Task` spawns another `Thread`. You can see that by checking `Thread.CurrentThread.ManagedThreadId` in the `Task` it will not be the ID of the `Thread` in which the `Task` was created and started. – Romano Zumbé Jul 11 '17 at 14:54
  • 1
    How would you check thread id in something like `FileStream.WriteAsync`? It returns Task but nowhere inside it there is a switch to different thread. It's just not true that "every Task spawns another thread", Task and Thread are different concepts. – Evk Jul 11 '17 at 14:58
  • *from the applications perspective every Task spawns another Thread* - that is completely false. – Ant P Jul 16 '17 at 08:29
  • @dukc because you can't just make a synchronous (e.g. CPU-bound) function asynchronous. It has to be asynchronous to begin with (e.g. IO-bound). You can fake it (by e.g. wrapping a background thread in a task) but that isn't really asynchrony. – Ant P Jul 16 '17 at 08:30