Search Unity

  1. How can our website serve you better? Give us your feedback. Take our survey and let us know.
    Dismiss Notice

Coroutine vs async method

Discussion in 'Scripting' started by Reeley, Jul 17, 2017.

  1. Reeley

    Reeley

    Joined:
    Feb 23, 2017
    Posts:
    40
    With Unity 2017.1 we have the opportunity to implement async methods. I never worked with them so im asking myself what are the advantages/disadvantages of Coroutines and async methods?

    Is one better than the other?
     
  2. Gizmoi

    Gizmoi

    Joined:
    Jan 9, 2013
    Posts:
    327
    Both fulfil different needs.
    Coroutines are useful for executing methods over a number of frames.
    Async methods are useful for executing methods after a given task has finished.

    e.g. async methods can commonly used to wait for I/O operations to complete.
    Coroutines can be used to move an object each frame.

    Unity devs have said they may replace Coroutines with an Async/Await style alternative, but they do not currently have any plans to, especially until they is a distinct reason to.
     
    PLDeveloper and Reeley like this.
  3. Reeley

    Reeley

    Joined:
    Feb 23, 2017
    Posts:
    40
    That makes sense. Lets say i want to Destroy a gameobject after 5 seconds. Is it better to use a coroutine or an async method?
     
  4. Scabbage

    Scabbage

    Joined:
    Dec 11, 2014
    Posts:
    268
    @Reeley Probably a coroutine. No faffing about in other threads and the code will likely be cleaner + more readable.
     
  5. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,481
    for that use a CoRoutine since it is all on the main thread and you simply want to wait X amount of time.

    I havent dealt with the Task, Task<T>, assync and await in unity yet, but i have for other pure .net or xamarin projects. What they are useful for is waiting on a long running task to complete. Such as things like waiting for the response to a REST API request, and than doing something with that response after it is complete, or really dealing with any long running task that does not need to be on the main thread.

    When it comes to unity and doing stuff over time, i would stick with coroutines only since that is what they were made for.
     
  6. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    6,102
    ExtraCat and PlayCreatively like this.
  7. Addyarb

    Addyarb

    Joined:
    Mar 2, 2014
    Posts:
    41
    Seems like I might be missing something here. I just tried to import System.Threading.Tasks and use the async keyword in Unity 2017.1. The namespace didn't exist, and I got a compiler error "Feature `asynchronous functions' cannot be used because it is not part of the C# 4.0 language specification".

    Perhaps this is a not-yet implemented feature?
     
  8. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,309
    You may have to change it to C# 4.6 in the Player Settings.
     
    cstooch and Addyarb like this.
  9. Addyarb

    Addyarb

    Joined:
    Mar 2, 2014
    Posts:
    41
    Thanks, that did the trick! Now to go experiment with these cool new features.

    I wonder if Unity/someone will do a write-up on the new C# 6 features and examples of where to use them? I assume there's some reason that they made the switch other than async capabilities. Maybe there's just inherent optimizations that come along with the new compiler?
     
  10. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,516
  11. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    async/await doesn't necessarily have to be for running async stuff:

    Code (csharp):
    1.  
    2.        public void StartTimer()
    3.        {
    4.            DoSomething(() => Console.WriteLine("Hello!"));
    5.        }
    6.  
    7.        public async void DoSomething(Action callback)
    8.        {
    9.            while (true)
    10.            {
    11.                await Task.Delay(1000);
    12.  
    13.                callback();
    14.            }
    15.        }
    16.  
    When used this way, it's pretty similar to coroutines. That said, I believe there's a bit more overhead than just using coroutines, so YMMV.

    I've written state machines and states using async/await as I found it pretty handy for those "just in case" scenarios where I needed to do async stuff in some state.
     
  12. Reeley

    Reeley

    Joined:
    Feb 23, 2017
    Posts:
    40
    Thats exactly what i wonder about, but i guess your right it will have more overhead because a Task has to be created.
     
  13. zsaladin

    zsaladin

    Joined:
    Jan 20, 2015
    Posts:
    11
    There is a library I made.
    It makes async/await and coroutine handled easily.

    Code (CSharp):
    1. using Asyncoroutine;
    2.  
    3. async void Awake()
    4. {
    5.     await new WaitForSeconds(1f);
    6.     Debug.Log("WaitForSeconds");
    7.  
    8.     await Task.Delay(1000);
    9.     Debug.Log("Delay");
    10.  
    11.     WWW www = await new WWW("http://google.com");
    12.     Debug.Log(www.text);
    13.  
    14.     await new WaitForSecondsRealtime(1f);
    15.     Debug.Log("WaitForSecondsRealtime");
    16.  
    17.     await UnityCoroutine();
    18.     Debug.Log("UnityCoroutine");
    19. }
    https://github.com/zsaladin/asyncoroutine

    Note : It's being still developed so it needs test.
     
  14. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,505
    General information that might help. Obviously all IHMO:

    o Unity isn't upgrading C# versions to get any of the new features. It's just general compatibility and bug-fixes.

    o The original command/concept is fork and sleep, which is 30 years old. Unity implemented their version as coroutines; C# is finally adding theirs.

    o Microsoft products (C#) generate buzz just because. People will talk up any new feature the way they'll get excited about new Team Fortress skins.

    o Sure, running on another thread can speed things up, but you pay for it with race conditions, and they're the worst.

    Instead of thinking that Unity is providing us with this new await command, I think of it as the TV cable channels that just came with the package.
     
  15. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    27,502
    I still haven't managed to use co-routines much, probably too old fashioned. I still drop by these threads to see if there's anything I'm missing.
     
  16. eventropy

    eventropy

    Joined:
    Oct 4, 2012
    Posts:
    249
    For anyone interested, I took a deeper dive into this topic and wrote about my findings here
     
    phobos2077, KelsoMRK and hippocoder like this.
  17. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    27,502
    Enjoyed the write up, thanks. Learned some cool things! It's good you accounted for Unity's game time as well.
     
  18. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,516
    Good stuff! Just a couple niggly points. You mentioned that yielding null in a coroutine does nothing when in actuality it waits a frame. You also mentioned this being part of C# 4.6. This is the version of the runtime, not the language. async/await is a part of C# 6.0 IIRC.
     
    phobos2077, hippocoder and eventropy like this.
  19. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,825
    Indeed a good explanation.

    To add some notes, the default logger behind Debug.Log is not necessarily throwing exceptions when it's called from other threads.

    In regards to the second snippet in 'Exception Handling', it's common practice to use the async keyword all the way up anyway.

    So this:
    Code (csharp):
    1. private void Start()
    2. {
    3.     SomeTask();
    4. }
    5.  
    6. private async Task SomeTask()
    7. {
    8.     ...
    9.     if ( some boolean expression)
    10.     {
    11.          throw new System.Exception (" ... ");
    12.     }
    13. }
    14.  
    should reallread
    Code (csharp):
    1.  
    2. private async void Start()
    3. {
    4.    /* await */ SomeTask();
    5. }
    That'll allow the Exception to bubble up correctly.

    Or you can now handle it yourself:

    Code (csharp):
    1.  
    2. private async void Start()
    3. {
    4.     try
    5.     {
    6.         /* await */ SomeTask();
    7.     }
    8.     catch (Exception exc)
    9.     {
    10.          // handle
    11.     }
    12. }
    13.  
    *Edit
    The /* await */ parts exist because the difference between non-awaiting and awaiting is also significant.
     
    Last edited: Sep 25, 2017
    KelsoMRK likes this.
  20. eventropy

    eventropy

    Joined:
    Oct 4, 2012
    Posts:
    249
  21. eventropy

    eventropy

    Joined:
    Oct 4, 2012
    Posts:
    249

    Gotcha. So you'd use 'async void' for all methods that call async methods but which you don't want to use 'await' with.

    I'm not sure if that's better than using a method like WrapErrors though. Maybe it is standard practice and I'm wrong, but I would have some concerns about doing it that way.

    Take the following code as an example:

    Code (csharp):
    1.  
    2. public class Foo : MonoBehaviour
    3. {
    4.     public async void DoSomething()
    5.     {
    6.         WaitABitAsync();
    7.     }
    8.  
    9.     async Task WaitABitAsync()
    10.     {
    11.         Debug.Log("Waiting 1 second...");
    12.         await new WaitForSeconds(1.0f);
    13.         Debug.Log("Done!");
    14.     }
    15. }
    16.  
    1. Marking DoSomething async suggests to any code calling these methods that it runs asynchronously even though it actually runs synchronously. This might be important if you call the method and then expect the state of the application to be a certain way after the method completes.

    2. Does the same rule apply to any code that calls Foo.DoSomething? Should those methods add the 'async' keyword to their methods too? If so, then it seems like the async keyword will end up everywhere. And if not, then what happens if I later decide to have DoSomething return Task instead? Then I have to find every method that calls it to ensure that those methods use the async keyword.

    3. The above example produces these warnings on compile:

    `warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.`
    `warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.`

    I could disable these warnings, but then I'm more likely to forget to add the await keyword when I actually intend to await.

    4. What convention would you use for methods like DoSomething that you mark as 'async void'? Usually you use the suffix for all methods that are marked async, but in this case they are synchronous.

    5. This practice makes the rule about always preferring 'async Task' to 'async void' a bit more confusing for people, because the rule then becomes "always prefer 'async Task' except when the method does not contain an await but calls async methods, then prefer 'async void'
     
  22. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    Cool article. One minor quibble I have is this:

    I agree for the most part, but I think an explanation could be clearer around the use of "async void". The way I see it, I see async void as an entry point into calling async methods and awaiting them. This is reinforced by the fact you can't await on an async void (because it doesn't return a task...)

    Beyond the entry point, then yes I agree it should be async Task all the way down. You definitely don't want to even try to call async Task methods from non async methods as that can quickly make the code confusing to read, write, and maintain. Let your "async void" methods be your wall between synchronous and asynchronous code.

    I haven't looked at all into exception handling using async/await in Unity, but in non-Unity apps I've written, I'd handle AggregateExceptions, for if you had nested exceptions being thrown. Kind of a pain in the ass to handle, but at least it's an option, unlike with coroutines. Is that the exception type being used in Unity? (Yeah I could try it for myself, but I'm lazy...)
     
  23. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,825
    This does also apply to your wrapper. If you call 'DoSomething' which internally calls your wrapper... what does the caller gain from it ? The compiler wouldn't even be able to tell that the calling code cannot expect that everything has run. So you'd work around that by allowing the registration of callbacks or implementing a flag / object to poll for.

    Also, you're also completely eliminate to custom-handle any exception, as they're all bound to your wrapper...

    And the thing is void cannot be awaited anyway, so you'll always call an 'async void' method synchronously, which means the caller would not and cannot even care about this being executed asynchronously.

    For instance, if you turn Unity's Start method into an async method, what does it change for the engine or the event pipeline?
    The engine itself is not going to await Start anywhere. It simply runs Start synchronously and everything that happens inside there - like awaiting something - is not a concern for the engine itself. It'll go back to list of object that need to run their Starts that have been called yet and continues its main work synchronously.

    Like stated above. 'void async' can only be called synchronously.
    I'm not sure whether a custom awaiter using GetAwaiter as an extension for several types is the best way to go.

    The warnings exist by default for a good reason. Please don't disable them, rather use them as a guide, so that you offer a good API and make sensefull use of the async methods.

    The question is not whether to disable, ignore or do something else with it. The question should be why are they async in the first place and secondly, why do you call them synchronously?

    Perspective of the API's user:
    If an API offers a method XYZAsync I'd expect that it runs async at some point and I'll be aware of that, in the end the name's primarily what tells you that this method is designed to run asynchronously rather than synchronously. No matter whether this is the 'good old' way using System.IAsyncResult or callbacks, events and/or polling.

    This also matches the part of the convention that you referred to in your write-up: Name the methods accordingly using the suffix 'Async'.

    You can offer the synchronous alternative as well, so that it'll no longer be a question whether to use
    await DoSomethingAsync or DoSomethingAsync,
    but rather
    await DoSomethingAsync or DoSomething.

    The actual behaviour:
    Calling a non-void async method synchronously has several implications: There's most-likely an await inside of that method (if not, why would it by async in the first place?) and this await is going to await the Task, if you want that or not.

    The result is, that your synchronously called async method returns, yet it is still awaiting something and has not completely finished. That's exactly the behaviour you described that you wanted to prevent. The caller might assume everything has been done, but in fact, it could still be running.
    Which brings us back to your initial question about async void and async Task and why async methods should be awaited, hence the warning.


    I'm not sure that there's a convention. As mentioned, the async keyword usually goes all the way up to the route of the calling chain and someone or something calls into it synchronously. It's kinda like the initial entry point for that particular chain of async methods.


    I'll try to post a practical example as to why this should not be a concern. Async void itself not do anything special, but it allows you to start a new chain of awaited actions.
     
    Last edited: Sep 25, 2017
  24. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,825
    Coming back to this with some examples.

    Code (CSharp):
    1.  
    2. private void Enter() { DoSomethingAsync(); }
    3. private async void EnterAsync() { DoSomethingAsync(); }
    4. private async Task DoSomethingAsync()
    5. {
    6.     await Task.Delay(2000);
    7.     if ( ... )
    8.     {
    9.         throw new System.Exception();
    10.     }
    11. }
    Neither of both Enter methods do make a lot of sense, except that EnterAsync can handle exceptions when awaiting. That's also a reason to let this go all the way up, you might need to handle exceptions that bubble up. In serios applications, you don't just wanna catch them in a generic (generic in terms of handler for everything) 'wrapper'.

    But, why would you want to call DoSomethingAsync without await in this case? It'll start to run async when it hits await Task.Delay(2000) and neither of the Enter methods will be able to tell if it's finished. The first one cannot, as it does not even allow to await (missing async), the second cannot, as it still calls it synchronously and also gets control back once the inner await is hit.
    Neither does yet another wrapper function to start off that async method without warning (these exist btw).

    The expectation that this is going to run completely synchronous is wrong, you'd need to append a blocking method call to the async Task method then, because that's effectively the same like running it completely synchronously.

    And if you need that, well... just forget about the async version and code / use a synchronous version directly. That pretty much solves the confusion about when to use one or the other IMO.

    What's the alternative to this to the starting wrapper?
    If you do not care about the result and error handling is not a thing, you can use Task.Run( ...) or the Task.Factory.StartNew(...) with more options to configure the task.
     
    Last edited: Sep 25, 2017
  25. Gizmoi

    Gizmoi

    Joined:
    Jan 9, 2013
    Posts:
    327
    Just wanted to say thanks for all that info, @Suddoha. I certainly feel like I understand async/await better!
     
  26. eventropy

    eventropy

    Joined:
    Oct 4, 2012
    Posts:
    249
    Yes, @Suddoha has schooled me on this topic :) It makes a lot more sense to me now. Thanks for the detailed explanation

    I think one thing that confused me was when you posted this code:

    Code (csharp):
    1.  
    2. private async void Start()
    3. {
    4.    /* await */ SomeTask();
    5. }
    6.  
    I thought you were suggesting that you should NOT include the await keyword here since it's commented out. But I think you're actually saying that you SHOULD use the await keyword here so that the exception bubbles up. This also addresses the compiler warnings.

    I can see why the wrapper I propose in the article isn't really necessary once you understand this, and actually might even be bad since as you say it forces one central place where exceptions are handled.

    Like @BlackPete says, 'async void' still has a purpose and that is as an explicit entry point for going from synchronous code to asynchronous code. The only drawback I see there is that this might result in a lot of methods that are `async void` and do nothing except call their `async Task` counterpart that has the Async suffix. I haven't written enough async code to know whether this would be annoying in practice or whether this would be a good thing, to maybe force there to always be a specific place for you to put custom exception handling. But a wrapper method might still be useful just for convenience when you don't really care about custom exception handling. Or do you think it's truly bad practice to use something like WrapErrors for cases like that?

    I'll update the article with my new understanding of all this
     
  27. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,825
    Yes i noticed. Yesterday when I had a look at my post again, I recognized this was a little confusing. Hence the edit at the end of that post. :) Calling SomeTask() synchronously does not make a lot sense there either, async void is really just like the entry point for you to start using await, which of course, should be used here.

    Well, it's not a real drawback, because you'd only do that if you really need to await something and this is usually the root of the whole chain of tasks to await.

    But this is also potentially error prone in one or the other situation. So sometimes you want to make sure this is going to wait for your task.

    Why? Consider the following program, which uses such a starter function and stops even before the task finished.
    Some heavy work is simulated and you want to write the result to a file.
    But: The file's never written, as Main calls the starter method synchronously (of course - it's async void) and that async void method awaits a Task - heavey work simulated with Task.Delay(...):

    Code (CSharp):
    1. class Program
    2. {
    3.     private const string PATH = @"D:\Repositories\test.txt";
    4.     private const int HEAVY_WORK_DURATION = 5000;
    5.  
    6.     private static void Main(string[] args)
    7.     {
    8.         StartHeavyWork();
    9.     }
    10.     private static async void StartHeavyWork()
    11.     {
    12.         await DoHeavyWorkAsync();
    13.     }
    14.     private static async Task DoHeavyWorkAsync()
    15.     {
    16.         await Task.Delay(HEAVY_WORK_DURATION);
    17.         System.IO.File.Create(PATH);
    18.     }
    19. }
    20.  
    This is a minimalistic program though.
    You could just code it synchronously. But let's pretend you only have the async versions of the API,
    you could then make it properly synchronous and prevent the program from closing itself by using
    Task.Run(() => StartHeavyWork()).Wait();
    or by eliminating the starter method and do
    DoHeavyWorkAsync().Wait();

    This blocks further execution, in this example it stops Main() from finishing before the task has been done.

    Now, I'm not telling you to use Wait() frequently. It blocks, and you usually don't want something to block unless you're on a top level like Main that necessarily needs to wait.

    Async is useful as you can just keep e.g. UI and other stuff responsive, without the need to spin up a Thread for every single task. It just kinda 'interrupts' execution in a method, brings back the control to the caller. If the caller is still awaiting a callee, it'll bring back control up to its own caller, so on and so forth.

    In Unity, this is could be a method like Start, which needs to get control back ASAP and run through so that the engine can keep processing all other objects.
     
  28. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    It's often easy to think of async void as "Fire and forget" tasks where it can go off and execute a bunch of awaits in the background* while you continue merrily on.

    *Not really the background as it's still on the same thread up until you perform a Task.Run, but you get the idea.

    So in the Main function, it's pretty easy to see that you've fired and forgotten something, and then you see that there's nothing stopping the program from ending at that point.

    If you don't want to block at the moment you called the async void function, you could store a local copy of the Task object returned by DoHeavyWorkAsync, which gives you an opportunity to check on it at a later time. Or just go with the simple isDone bool solution :p (Although I'd really encourage you to look at caching the Task... it can be handy in ways you didn't even realize until later on...)
     
    Suddoha likes this.
  29. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,825
    Yes, exactly. It acts like fire and forget, hence the example. It's not always desired and especially not when you care about the result.

    If an async void, let it be Unity's Awake or Start, runs a task, you should just make it async void and await everything, especially to handle the exceptions.

    What I wanted to show is, that running methods synchronously with the expectation that it completes before the method continues, needs some kind of blocking / polling.

    Introducing extra methods to run these awaitable tasks does only hide important information for users of an API and eliminating these intermediate methods makes the async keyword move up in the call hierarchy, naturally, which is rather good than bad I'd say.
     
    BlackPete likes this.
  30. phobos2077

    phobos2077

    Joined:
    Feb 10, 2018
    Posts:
    269
    So what's the consensus on using Coroutines vs async/await in 2019? Is there any reason to consider sticking to good-old coroutines or some scenarios where they fit better or it's just a matter of learning curve and people being used to the old ways and for new projects we should never use Coroutines?
     
  31. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    27,502
    They're both far below DOTS + Jobs for performance because:

    1. coroutines arent threaded, they just simply store some variables and run on main thread when the time is elapsed for them to do so. Purely programmer preference, so there is no point in discussing it in that context.

    2. async could be, but it will fight Unityengine for resources if you use in that manner...
    Are you just wanting a reaction style of code or?

    Because many people think this is going to be a performance improvement. If for performance you'd be best off with Jobs because then it won't fight UnityEngine for resources.
     
  32. phobos2077

    phobos2077

    Joined:
    Feb 10, 2018
    Posts:
    269
    What DOTS has to do with this? It's basically a new Unity 2.0 game engine which is far from being ready to use as a main tool and not just for solving specific performance tasks. For normal game development I'd better stick to GameObjects for now.

    Regarding the original topic, I can't say I'm asking for maximum performance, but a general rule of thumb. What are the benefits of Coroutines? (except that they conveniently kill themselves when game object is destroyed or deactivated, but it's not enough in terms of cancellation in most cases anyway). Benefits of async/await to my knowledge:

    - Can return values
    - Better exception handling
    - Don't require GameObject to work
    - It is a standard way of async programming in C#

    I haven't used them in a project yet though, these points are based purely on the quick research. I'll appreciate any informed comment on those.

    Regarding the "will fight Unityengine for resources if you use in that manner" - not sure what you mean. By default these tasks are running on the main UI/game thread, there's no multi-threading involved unless you do so. Also there are more efficient alternatives to Task<T> (UniTask). Please correct me where I'm wrong :)
     
  33. BlackPete

    BlackPete

    Joined:
    Nov 16, 2016
    Posts:
    970
    It really depends. For simple things that don't need to run in different threads, stick with coroutines (or heck create an IEnumerator and move it yourself, that's pretty much what coroutines boil down to anyway).

    If you really need multithreading, then async/await works just fine. However, you still need to be mindful of how you're pressing the garbage collector when creating Tasks all over the place. So it's not just something to drop in place of coroutines. You can lessen this by using cached tasks, but that's another topic.

    You'd still need to consider what you're doing then choose the appropriate tool for the job. And that's all they are -- different tools. It doesn't really make sense to say "Tool A is always better than Tool B". In fact, there's nothing stopping you from using both at the same time.
     
  34. phobos2077

    phobos2077

    Joined:
    Feb 10, 2018
    Posts:
    269
    I don't need multi-threading, as I said, it's not about performance or anything like that. I'm just considering if it's a good practice to dump coroutines completely in favor of async/await, since it's the normal C# way of writing async code (+ other benefits mentioned above).
    Regarding the garbage issue, there are ValueTasks and also UniTasks which supposedly fix this, but I have no experience using any of them.
     
  35. EZaca

    EZaca

    Joined:
    Dec 9, 2017
    Posts:
    20
  36. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,363
    Classic, people mix up multi threading and async programming all the time. By default they are not the same thing. In unity coroutines you always execute on the main renderer thread period.

    Tasks are a different beast all together, it's up to the scheduler and the task it self if multi threading is involved. Take Task.Delay as as en example, it spawns a TaskCompletionSource and returns this to the caller. But it also spawns a timer on the thread pool that will await the timespan and then invoke the complete source which will signal the scheduler to advance.

    Using TaskCompletionSource could be one to one mapped to YieldInstructions in unity corotuines and not a single thread other than main thread is involved.

    Edit: async programming is never about performance. In coroutines it's about executing async code in a syncrounous manner, purely for code readability and maintainability reasons.

    Tasks solve this problem too. But in the corporate world they solve a much bigger problem, resource hogging. For example there is no need for a rest service call to wait for the SQL database to complete its IO and hold that thread. It's much better to return the thread to the thread pool and let the next caller be served. When the SQL query is done you can resume your work on a new thread. Tasks is almost always slower per caller than standard blocking calls, but you can serve many more callers without complete killing performance
     
    Last edited: Oct 25, 2019
    Macellomatik, EZaca and phobos2077 like this.
unityunity