none
Async, Await, Tasks, and Returns... RRS feed

  • Question

  • Okay,

    I am missing something that my old school brain of handing threads, background workers, and the like is just not grasping.

    • async void X() {}
      this is supposed to be for event handlers only in order to "await" a Task in a multi-threaded environment
    • async Task X() {}
      this is for asynchronous methods that don't have a return value
    • async Task<T> X() {}
      this is for asynchronous methods that have a return value
    • Task X() and Task<T> X()
      these are methods that can be called with "await", but can't "await" themselves.

    Question:

    I have a method that i want to run asynchronously, but Visual Studio complains every which way i do it.

    async void button1_click(sender, e) 
    {
        this.MyData = await this.LoadData();
    }
    
    //This complains about the return type not being a "Task<MyDataObject>", but i can't "create" a "Task<>"
    private Task<MyDataObject> LoadData() 
    {
        MyDataObject mdo = new MyDataObject();
        this.LoadFromDb(mdo);
        return mdo;
    }
    
    //This complains that LoadData doesn't "await" anything, so remove the async, but it works
    private async Task<MyDataObject> LoadData() 
    {
        MyDataObject mdo = new MyDataObject();
        this.LoadFromDb(mdo);
        return mdo;
    }   
    

    That's my issue, straight forward.  I understand threads, the CancelationTokens, synchronizing for report progress feed back, all that jazz.

    I mean, is it the standard approach to do this:

    private Task<MyDataObject> LoadData()
    {
        MyDataObject mdo = new MyDataObject();
        this.LoadFromDb(mdo);
        return Task.FromResult(mdo);
    }

    Are we only supposed to use the "async" if we're going to "await" in the method, and thus if that method does not need to await, we use the "FromResult" statement on Task?

    Thanks

    Jaeden "Sifo Dyas" al'Raec Ruiner


    "Never Trust a computer. Your brain is smarter than any micro-chip."
    PS - Don't mark answers on other people's questions. There are such things as Vacations and Holidays which may reduce timely activity, and until the person asking the question can test your answer, it is not correct just because you think it is. Marking it correct for them often stops other people from even reading the question and possibly providing the real "correct" answer.

    Wednesday, October 30, 2019 5:56 PM

Answers

  • I agree with your signature summaries except this one.

    Task X() and Task<T> X()

    This just means you're calling a method that returns a task. To be honest I see this only useful in utility functions that are designed to help build up tasks or wait for the results. 

    Async/await is always tied together. You cannot use one without the other. Async is simply an indicator for the compiler that you intend to await inside the method for some other async method to finish. It doesn't mean anything else. Using `Task` does not require `async`. If you never need to await inside a method then you don't mark it async. It can still return `Task` though.

    //No await so no async
    Task MakeThemWait () 
    {
       //No reason to await this as we aren't doing anything else
       return Task.Delay(1000);
    }
    
    async Task DoSomeWork ()
    {
       //Make them wait
       await MakeThemWait().ConfigureAwait(false);
    
       //Do more work
    }

    The thing for understanding async/await is to realize it is just compiler sugar. It allows you to replace common blocks of code with a simpler syntax. So let's look at a more realistic example.

    Task DoWork ()
    {
       //This is running on the calling thread
       ...
    
       //Call something that is async
       var task = Task.Run(DoLongWork);
    
       //After that work completes we need to do some more stuff
       task = task.ContinueWith(ThenDoMoreWork);
     
       //When that completes do some more work
       task = task.ContinueWith(YetMoreWork);
    
       return task;
    }

    In this method we return Task to indicate that this method is doing some work that will take a while. The only reason we're returning `Task` is because we aren't going to wait for it to finish in this method but the caller probably wants to know when it is done. That is the purpose of `Task`. It is effectively a notification object.

    The most confusing thing about the above code is that it is unclear what is being done in the method itself and what is being done after the task finishes a block of work. That is why the `ContinueWith` method exists. It tells the runtime to continue with some more code after the previous code completes. Anything not inside the `ContinueWith` call will run as part of the original method call (hence before the task completes). This is confusing so async/await was added to make this more easy to see. Here's the equivalent version.

    async Task DoWork ()
    {
       //This is running on the calling thread
       ...
    
       //Wait for some other work to complete 
       await Task.Run(DoLongWork);
    
       //After that work completes we need to do some more stuff
       //This may or may not run on the calling thread - depends upon ConfigureAwait...
       await Task.Run(ThenDoMoreWork);
     
       //When that completes do some more work
       await Task.Run(YetMoreWork);
    }

    The benefit of async/await is that you can now "continue" code without having to use the `ContinueWith` method. Behind the scenes the compiler rewrites your code to the earlier version. Also notice that you are violating the run of C# that says that if a function has a return type of Task then you must return one yet we didn't here. The compiler implicitly returns Task (or equivalent) from the method and will handle converting any `return` statements you do have to the task equivalent. This keeps your code clean.

    So, the general rule of thumb:

    •  Return Task if you do not need to wait on the results of a child async call (that includes continuations)
    •  Use async on the method decl and await any calls to child async calls if you need to do work after the task completes. Return type is the return type before wrapping in a task.
    • If you use `async` but have no `await` in your method then remove `async`, change the return type of the function to `Task` (or equivalent) and return the task from the child async call you are making.

    Now to your example.

    //There is no use of task here so it shouldn't return Task<T>
    private Task<MyDataObject> LoadData() 
    {
        MyDataObject mdo = new MyDataObject();
        this.LoadFromDb(mdo);
        return mdo;
    }
    
    //There is no await here so no need to use it
    private async Task<MyDataObject> LoadData() 
    {
        MyDataObject mdo = new MyDataObject();
        this.LoadFromDb(mdo);
        return mdo;
    }   
    
    //Wrapping an existing sync method in async
    private Task<MyDataObject> LoadDataAsync() => Task.Run(LoadData);

    Note that wrapping a sync method in async changes the behavior a little but you probably won't notice. In the above example you aren't doing anything with the results so you don't need async/await. Since the method itself isn't async you can wrap it in `Task.Run` to make it so.

    If however you actually had async work to do in your load then things are different.

    //Assuming existence of Task LoadFromDbAsync ( MyDataObject )
    
    //You aren't doing anything with the response so don't wrap it
    private Task<MyDataObject> LoadData() 
    {
        MyDataObject mdo = new MyDataObject();
        return this.LoadFromDbAsync(mdo);
    }
    
    //Need to do additional work...
    private async Task<MyDataObject> LoadData() 
    {
        MyDataObject mdo = new MyDataObject();
        await this.LoadFromDbAsync(mdo);
    
        //Do more work
        return mdo;
    }   


    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, October 30, 2019 6:43 PM
    Moderator

All replies

  • I agree with your signature summaries except this one.

    Task X() and Task<T> X()

    This just means you're calling a method that returns a task. To be honest I see this only useful in utility functions that are designed to help build up tasks or wait for the results. 

    Async/await is always tied together. You cannot use one without the other. Async is simply an indicator for the compiler that you intend to await inside the method for some other async method to finish. It doesn't mean anything else. Using `Task` does not require `async`. If you never need to await inside a method then you don't mark it async. It can still return `Task` though.

    //No await so no async
    Task MakeThemWait () 
    {
       //No reason to await this as we aren't doing anything else
       return Task.Delay(1000);
    }
    
    async Task DoSomeWork ()
    {
       //Make them wait
       await MakeThemWait().ConfigureAwait(false);
    
       //Do more work
    }

    The thing for understanding async/await is to realize it is just compiler sugar. It allows you to replace common blocks of code with a simpler syntax. So let's look at a more realistic example.

    Task DoWork ()
    {
       //This is running on the calling thread
       ...
    
       //Call something that is async
       var task = Task.Run(DoLongWork);
    
       //After that work completes we need to do some more stuff
       task = task.ContinueWith(ThenDoMoreWork);
     
       //When that completes do some more work
       task = task.ContinueWith(YetMoreWork);
    
       return task;
    }

    In this method we return Task to indicate that this method is doing some work that will take a while. The only reason we're returning `Task` is because we aren't going to wait for it to finish in this method but the caller probably wants to know when it is done. That is the purpose of `Task`. It is effectively a notification object.

    The most confusing thing about the above code is that it is unclear what is being done in the method itself and what is being done after the task finishes a block of work. That is why the `ContinueWith` method exists. It tells the runtime to continue with some more code after the previous code completes. Anything not inside the `ContinueWith` call will run as part of the original method call (hence before the task completes). This is confusing so async/await was added to make this more easy to see. Here's the equivalent version.

    async Task DoWork ()
    {
       //This is running on the calling thread
       ...
    
       //Wait for some other work to complete 
       await Task.Run(DoLongWork);
    
       //After that work completes we need to do some more stuff
       //This may or may not run on the calling thread - depends upon ConfigureAwait...
       await Task.Run(ThenDoMoreWork);
     
       //When that completes do some more work
       await Task.Run(YetMoreWork);
    }

    The benefit of async/await is that you can now "continue" code without having to use the `ContinueWith` method. Behind the scenes the compiler rewrites your code to the earlier version. Also notice that you are violating the run of C# that says that if a function has a return type of Task then you must return one yet we didn't here. The compiler implicitly returns Task (or equivalent) from the method and will handle converting any `return` statements you do have to the task equivalent. This keeps your code clean.

    So, the general rule of thumb:

    •  Return Task if you do not need to wait on the results of a child async call (that includes continuations)
    •  Use async on the method decl and await any calls to child async calls if you need to do work after the task completes. Return type is the return type before wrapping in a task.
    • If you use `async` but have no `await` in your method then remove `async`, change the return type of the function to `Task` (or equivalent) and return the task from the child async call you are making.

    Now to your example.

    //There is no use of task here so it shouldn't return Task<T>
    private Task<MyDataObject> LoadData() 
    {
        MyDataObject mdo = new MyDataObject();
        this.LoadFromDb(mdo);
        return mdo;
    }
    
    //There is no await here so no need to use it
    private async Task<MyDataObject> LoadData() 
    {
        MyDataObject mdo = new MyDataObject();
        this.LoadFromDb(mdo);
        return mdo;
    }   
    
    //Wrapping an existing sync method in async
    private Task<MyDataObject> LoadDataAsync() => Task.Run(LoadData);

    Note that wrapping a sync method in async changes the behavior a little but you probably won't notice. In the above example you aren't doing anything with the results so you don't need async/await. Since the method itself isn't async you can wrap it in `Task.Run` to make it so.

    If however you actually had async work to do in your load then things are different.

    //Assuming existence of Task LoadFromDbAsync ( MyDataObject )
    
    //You aren't doing anything with the response so don't wrap it
    private Task<MyDataObject> LoadData() 
    {
        MyDataObject mdo = new MyDataObject();
        return this.LoadFromDbAsync(mdo);
    }
    
    //Need to do additional work...
    private async Task<MyDataObject> LoadData() 
    {
        MyDataObject mdo = new MyDataObject();
        await this.LoadFromDbAsync(mdo);
    
        //Do more work
        return mdo;
    }   


    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, October 30, 2019 6:43 PM
    Moderator
  • //This complains about the return type not being a "Task<MyDataObject>", but i can't "create" a "Task<>"
    private Task<MyDataObject> LoadData() 
    {
        MyDataObject mdo = new MyDataObject();
        this.LoadFromDb(mdo);
        return mdo;
    }

    The above code needs to return a Task<MyDataObject> to compile. There are a few ways to do this.

    Technically instead of returning mdo directly, you could return Task.FromResult(mdo) which will create a completed task with the mdo object as the result. This will compile and run. But if LoadFromDb does blocking work, your UI will not be responsive during the load operation.

    Alternatively, when a method is marked as async, the object you return is automatically wrapped in a task. So technically you could mark the method async and it should compile although you will get a warning indicating the method lacks 'await' operators and will execute synchronously.

    When you use the await keyword in a method, that method will also wrap the result in a Task<> or simply Task if there is no return value.

    The real question for you to consider is: does/can LoadFromDb do its job asynchronously or does it block?

    Drill down the calling hierarchy to where it invokes the method to fetch from the database. Is there a version of the same method that returns a Task<MyDataObject>? If so await it in the calling method then push the async up the calling hierarchy until you can call "await this.LoadFromDb(mdo)" in LoadData() (noting that by convention the method should be called "LoadFromDbAsync"). This will ensure your UI stays responsive while the data is loaded from the database. 

    If it is blocking, you could wrap the method in Task.Run(this.LoadFromDb(mdo)) which will return a task that can be awaited for completion. But be warned generally wrapping blocking work in Task.Run can introduce performance problems and potentially lock up the ThreadPool if you start to use this approach everywhere. So while it technically it will work in one small case, when scaled up it could cause problems.

    Friday, November 1, 2019 3:37 AM
  • The thing for understanding async/await is to realize it is just compiler sugar. It allows you to replace common blocks of code with a simpler syntax. So let's look at a more realistic example.
    Task DoWork ()
    {
       //This is running on the calling thread
       ...
    
       //Call something that is async
       var task = Task.Run(DoLongWork);
    
       //After that work completes we need to do some more stuff
       task = task.ContinueWith(ThenDoMoreWork);
     
       //When that completes do some more work
       task = task.ContinueWith(YetMoreWork);
    
       return task;
    }

    In this method we return Task to indicate that this method is doing some work that will take a while. The only reason we're returning `Task` is because we aren't going to wait for it to finish in this method but the caller probably wants to know when it is done. That is the purpose of `Task`. It is effectively a notification object.

    Would this be comparable to the idea of the "black-box" delayed processing of "IEnumerable"?  
    Like, for example you have:

    var results = myList.Where(i => i.HasValue).Select(t => t.TransformValue());
    foreach(var x in results) 
    {
      DoSomething(x);
    }

    The var results just holds lambda method references and they aren't executed until this code reaches the "in" operator of the foreach loop, at which point the internal IEnumerable processor kicks in and the individual "HasValue" properties are evaluated as part of the Where() clause, and the TransformValue() is executed iteratively as part of the Select transform operation.
    Is this the concept behind "Task"?  That Task is, for lack of a better analogy, like an "IEnumerable" of "Actions"?  So that until the "FromResult" is executed, all of the actions (Tasks) contained within the returned Task of the method are then executed.  The Async/await pattern does
    compiler sugar around the "FromResult" aspect which then executes those tasks in successive order in a separate thread from the thread pool?

    The most confusing thing about the above code is that it is unclear what is being done in the method itself and what is being done after the task finishes a block of work. That is why the `ContinueWith` method exists. It tells the runtime to continue with some more code after the previous code completes. Anything not inside the `ContinueWith` call will run as part of the original method call (hence before the task completes). This is confusing so async/await was added to make this more easy to see.

    I mean, that seems odd.  Like take this example:

    public Task DoSomething() 
    {
      Task.Run(DoWork);
       
      Console.Writeline("Hello");
      
      Task.ContinueWith(DoMoreWork);
    
      Console.Writeline("World");
    
      Task.ContinueWith(YetMoreWork);
    
      Console.Writeline("Done.");
    }
    
    void DoWork() 
    {
      int result = 0;
      for (int i = 0; i < 10000; i++)
      {
        result += i;
      }
      Console.Writeline(result);
    }
    
    void DoMoreWork() => DoWork();
    
    void YetMoreWork() => DoWork();
    
    /*
     * Does your explanation mean the out put could be:
     * Hello
     * World
     * 49995000
     * Done.
     * 49995000
     * 49995000
     */

    This seems to suggest from your description that upon the execution of Task.Run() a thread is already spawned.  Like I said I'm coming from the older Thread and BackgroundWorker style of thread management, so I'm used to "ThisIsMyThreadEntryPoint" as a method, and then i can use Semaphores or WaitEvents to determine when the thread has completed. (BackgroundWorker makes completion notification quite simple.)

    From your description it appears that async/await isn't necessary for thread management, which i thought was the purpose of async/await to make threaded methods easier to read/write/use instead handling complicated callbacks.  In most circumstances this appears to be true, but again my problem is with calling the method in a thread space, not necessarily defining it.

    //There is no use of task here so it shouldn't return Task<T>
    private Task<MyDataObject> LoadData() 
    {
        MyDataObject mdo = new MyDataObject();
        this.LoadFromDb(mdo);
        return mdo;
    }
    
    //There is no await here so no need to use it
    private async Task<MyDataObject> LoadData() 
    {
        MyDataObject mdo = new MyDataObject();
        this.LoadFromDb(mdo);
        return mdo;
    }   
    
    //Wrapping an existing sync method in async
    private Task<MyDataObject> LoadDataAsync() => Task.Run(LoadData);

    So this is where the confusion sets in.  The reason that my first method was defined as a "Task" method is because I want to "await" it.  It is the method that is my "Thread Callback" method. 

    async void button1_click(sender, e)
    {
       await this.MyData = DoSomething();
    }
    
    private Task<MyDataObject> DoSomething() 
    {
       MyDataObject mdo = this.LoadMyDataObject();
       PerformStep1(mdo);
       PerformStep2(mdo);
       PerformStep3(mdo);
       return mdo; //<-- this is where I can't do this
    }

    The above example describes a general example of what i'm doing with threads.  The load process is going to take a while.  So I want progress updates and other feed back to the user while I'm running my load computations. Any computations that could "freeze" the UI, for long enough periods of time of course, i want to run in a thread so the UI can remain interactive.  For cancellation, progress updates, status messages, and the like.  There could be multiple steps that are appropriately divided into smaller methods for SOLID design.  But the whole process is supposed to be run in it's own thread space so that the UI thread can still be interacted with and receive messages. 

    The way it's written above doesn't work, because if I define the DoSomething() method as "async" it complains that I'm not using "await" within it.  However, without the "async" directive, I can't return the MyDataObject directly, because without the "async" keyword it expects a Task<MyDataObject>.  If I don't make the method a Task<MyDataObject> i can't await it.  The "await" keyword DEMANDS a "Task" type and can only be used in an "async" method. 

    Unless you're suggesting that when I'm not going to "await" inside a method, don't return Task<MyDataObject> but return MyDataObject and call it with Task.Run<>.

    async void Button1_click(sender, e)
    {
      this.MyData = await Task.Run(this.LoadData)
    }
    
    MyDataObject LoadData() 
    {
      MyDataObject mdo = this.LoadData();
      this.ProcessData(mdo);
      return mdo;
    }

    I totally understand the association between an async method awaiting a call from within.  My issue is within the relationship between async/await and Task.  Each time i want to say:  "This operation should be in a separate thread." i then have to battle with making every method call a Task so it can be awaited, or I have to do crazy lambda's within the Task.Run() method, just to get the syntax compiler to shut up about these "wacky" organizational patterns.

    99.9999% of all of my threaded operations are threaded from the main UI thread, so i can maintain control of the UI while the operation is proceeding.  Since VS complains about using "async void" except when it's an event handler, that means that my event handlers have to be more complicated.  If two different events (menu item click, and button click) execute the same operation, I'm duplicating code then.  So i would like to have:

    private async void LoadData()
    {
      this.MyData = await this.LoadDataAsync();
      this.MyData.ProcessData();
    }
    
    private Task<MyDataObject> LoadDataAsync()
    {
      MyDataObject mdo = new MyDataObject();
      using (DbConnection conn = this.GetConnection())
      {
         using (DbDataReader reader = CreateReader(conn)) 
         {
           mdo.LoadFromReader(reader);
         }
      }
      //return mdo; <-- Can't do this because it isn't "async"
      return Task.FromResult(mdo);
    }
    
    async void _openButton_Click(sender, e)
    {
      await this.LoadData();
    }
    
    async void _openMenuItem_Click(sender, e)
    {
      await this.LoadData();
    }

    Now mostly, i think that just looks cleaner, and seems to flow.  But the async void at the top raises warnings, so to remove them i have to make the method return a Task. I can't return the direct return type from the Task<MyDataObject> (because it isn't async) so I have use the FromResult() to get that method to compile.  As well, in my opinion using the "Task.Run()" statement to run a method when I feel i should just be able to "await" that method as it is the async operation. 

    It's like "async" is in the wrong place, or I'm thinking forward instead of backwards.  Instead of awaiting within the "async" method, I should be awaiting the "async" method.  Like, in order to use the "await" keyword, the method I'm awaiting should be flagged "async".  Not the other way around.  The way it is, await needs a "Task", and in order to use "await" the containing method must be "async". 

    Your descriptions were very helpful, but only insofar as helping me ask better questions. 

    So i guess the best question is:  where is the Thread entry point.  At "await" or at "async"?  I think it's at "await", but perhaps that's too simple of an evaluation.

    Thanks

    J"SD"a'RR


    "Never Trust a computer. Your brain is smarter than any micro-chip."
    PS - Don't mark answers on other people's questions. There are such things as Vacations and Holidays which may reduce timely activity, and until the person asking the question can test your answer, it is not correct just because you think it is. Marking it correct for them often stops other people from even reading the question and possibly providing the real "correct" answer.




    • Edited by JaedenRuiner Wednesday, November 13, 2019 6:54 PM
    Wednesday, November 13, 2019 6:41 PM
  • "Would this be comparable to the idea of the "black-box" delayed processing of "IEnumerable"?  "

    Let's make a clarification here, IEnumerable has no black box processing whatsoever. While you see it with IEnumerable it is not actually tied to that at all. Any method that returns any type can do that. For example `DbConnection` instances seem like creating the instance should open the connection but they don't. They open the connection only when you need it. It is an implementation/optimization detail. That is how most IEnumerable methods are implemented as well. The method (GetEnumerator) just needs the object that implements it so most methods return (via iterator syntax) an instance of a type that will eventually enumerate. This is for performance reasons but it is not required to do so. You mentioned `Where` and it does (like most LINQ methods) implement deferred execution but you can write your own version that doesn't.

    public static class MyExtensions
    { 
        public static IEnumerable<T> WhereThatExecutesImmediately<T> ( this IEnumerable<T> source, Func<T, bool> predicate )
        {
            var items = new List<T>();
    
            //Filter the results before we return
            foreach (var item in source)
                if (predicate(item))
                    items.Add(item);
    
            return items;
        }
    }

    This is programming 101 so there is no sugar here. The only place the compiler sugar pops up is if you use the C# iterator syntax. In that case the compiler does generate a nested type that implements the logic, but if you don't use iterator syntax then you aren't using this feature. In general it is recommended that you do not make any assumptions about when an IEnumerable<T> returning method actually evaluates the items.

    "This seems to suggest from your description that upon the execution of Task.Run() a thread is already spawned"

    Threads and tasks are not the same thing. It is important to keep that in mind. A task can execute on the calling thread, synchronously and therefore be no different than any other method call. Or it may be called on a worker thread. It is up to the task scheduler that is being used. The default task scheduler provided by .NET uses the thread pool to execute tasks so the "thread" that the task runs on is probably already running, just waiting for work. However if you run out of threads then it will spawn another one. How long that thread lasts is up to the scheduler. It is an implementation detail. In general we assume that a task will run on a worker thread provided by the scheduler but there are exceptions when it won't. The nice thing about the interface though is that you shouldn't care.

    "From your description it appears that async/await isn't necessary for thread management, which i thought was the purpose of async/await to make threaded method easier to read/use instead handling complicated callbacks. "

    Again, tasks and threads are not the same thing. The purpose of async/await is to take Task-based code and make it easier to write by being able to mix code that runs on the task with code that runs after it. Whether that code is run on the current thread or a different one is up to the scheduler, for the most part. When working with tasks you really need to stop trying to line them up with how threads work. While they generally rely on threads that is completely an implementation detail. I think another good way of looking at this is with disk IO. Do you care about whether the data you just wrote is saved to disk immediately or buffered up and sent later? Provided the data is guaranteed saved you shouldn't care. Same thing with tasks. You don't care where they run, you just want them to run.

    "So this is where the confusion sets in.  the reason that the first method is defined as a "Task" method is because I want to "await" it."

    The first method doesn't do anything with tasks so there is no benefit in returning Task for it. It will run synchronously anyway.

    "So i want progress updates and other feed back to the user while i'm running my load comptations, or any computations that could "freeze" the UI."

    UIs are always special cases in the tasking world. UIs are the only reason why you are allowed to have a void-returning async method as well. The problem is with how the UIs work. Since you are using Winforms we'll work with that. Suppose the user clicks a button and you start some long operation. Until that operation completes the UI is blocked. There is nothing you can do to change that rule. So you need to circumvent it. To circumvent it you don't do the long operation on the UI thread but instead put it into a task. However the UI is still blocked so you need to return. You cannot change the method signature (because it is an event handler) so you cannot return anything. So your event handler returns and the UI moves on but quietly behind the scenes that task is still running. When it completes you want to do some more work. That is where either continuations (the old approach) or await comes in. The compiler rewrites the code to be called after the task completes. Remember the UI thread has already moved on. The runtime needs to know which thread to execute this code on. Because it doesn't know the default is to run on the original thread (which it captured during the initial call). In order to run code on the UI thread the runtime has to effectively stop the UI thread that is currently running, run your task code and then resume the UI thread. This is what the sync context that WInforms (and others) implement. They handle that magic. So the code after the await is running on the UI thread so it is stalled again. If you eventually await for yet more stuff then the UI thread is free to move on again until the next task completes. Rinse and repeat. 

    I think a good way of seeing this in action is adding log entries to your code before and during awaited work. Dump the Id of the managed thread associated with the current code and you'll see it bouncing around. Note that all this is relying on the understanding that you don't deadlock the UI. This is really easy to do and there are lots of articles on how easy it is to do and how to prevent it. Basically if the UI thread blocks for any reason (IO, task, etc) then none of the tasks that complete and want to resume on the UI thread can run either. I recommend you read up on Winforms and tasks guidelines to avoid deadlocking your code.

    "i then have to battle with making every method call a Task so it can be awaited"
    Correct. The mantra is "async all the way". Once you make a method async you need to be using async all the way down the stack from there. While technically not required you'll find it is often easier to do. Most people start with their data layer and make it async. Then they move to the business layer. Lastly they update the UI to properly work with async. It is pretty much all in.

    "If two different events (menu item click, and button click) execute the same operation, i'm duplicating code then"

    I disagree with this. If the event handlers use the same signature then a single method can be used. If they have different signature then naturally you'd need different methods but they can both call the same underlying functionality.

    //Do not async/await in a synchronous method
    private MyDataObject LoadData()
    {
      MyDataObject mdo = new MyDataObject();
      using (DbConnection conn = this.GetConnection())
      {
         using (DbDataReader reader = CreateReader(conn)) 
         {
           mdo.LoadFromReader(reader);
         }
      }
     
      return mdo; 
    }
    
    private Task<MyDataObject> LoadDataAsync()
    {
       //Since you already have a sync method that does
       //the work, just wrap it
       return Task.Run(() => LoadData());
    }
    
    //Assign this handler to _openButton and _openMenuItem's Click handler
    async void OnLoadData ( object sender, EventArgs e )
    {
       //This will start the work in a task and return immediately
       await this.LoadDataAsync();
    
       //This is running on the UI thread
    }
    Early on you'll probably have your async methods just calling the sync versions. But eventually as you go all in you'll start needing to have your sync methods call async versions. At that point you swap the implementation. The async version does all the work (using async/await instead of Task.Run) and your sync version calls the async version and uses either `Result` or `Wait` to block for the results.

    "So i guess the best question is:  where is the Thread entry point.  At "await" or at "async"?  I think it's at "await", but perhaps that's to simple of an evaluation."

    Again, stop thinking about this in terms of threads. But if I were to attempt to answer your question it is at the point you call Task.Run (because you pass it the "entry point"). In async/await code the compiler puts that at each "await" effectively. The `async` keyword on the method has no impact on the runtime behavior of the app. It is strictly there to tell the compiler (and devs) that this method is async/await. The language team felt that an explicit keyword would help avoid confusion (similar to the fact that we need `override` on a method that is already `virtual` or `out` on an out argument). The `await` code is what the compiler is rewriting.


    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, November 13, 2019 7:36 PM
    Moderator
  • Okay, I think we are getting way into semantic weeds here. I've been programming for over 25 years, so I know the concepts.  I may use a short hand like "black box" to reference aspects of third party systems that are performing operations behind the scenes. I've written my own memory manager before, so I know what goes into allocation of memory, but when I write "new object()" what is happening there is a "black box" of memory allocation that I don't need to know about or really care any more, I know it works. So, I fully understand and know about yield return, and deferred execution, and all that.  So I don't need a refresher course. 

    What I'm trying to understand is a break down of a newer syntax for achieving already existing concepts that I know the old way to use. 

    The concept is:  Threads.

    Multiple threads can run simultaneously, in order to break down the work.  I've used AsParallel() which made complete sense in how to use it because I already understood threads.  I've written systems that do the same thing as AsParallel(), because at the time I had to write it, but when the new AsParallel() option became available, i could achieve the same result without having to reinvent the wheel.  Now, Call it my control-freak nature, but I'm the programmer so the computer does what I tell it.  However, there are vast third party libraries and always advancing language specifications that I didn't write.  So I have to learn the nature of them and their associated concepts to be able to use them. For example you said:

    The first method doesn't do anything with tasks so there is no benefit in returning Task for it. It will run synchronously anyway.

    This is a categorically false statement.  Whether the method uses tasks or not has nothing to do with it's synchronous or asynchronous execution. I have reliably proven in the debugger that when I "await" a method, whether that method returns Task or if i use Task.Run(), the thread Id is different than the main thread.  It is executing on a worker thread.  Which by definition is "asynchronous execution".  Now, if i don't await that method, then it will run synchronously.  So there is an entry point somewhere to when it is threaded and when it isn't.

    The difference being if a method returns Task I have to "await" it to get it to run as I expect, because when I try to run it synchronously it doesn't behave like a "void" method because it is returning a "Task".  Which i believe answers one of my questions, as you covered later in your response, that the method is "defined" synchronously so therefore to execute it asynchronously, I should use "Task.Run(LoadData())" and await that instead of making the method return a task when it isn't using them.

    Threads and tasks are not the same thing. It is important to keep that in mind. A task can execute on the calling thread, synchronously and therefore be no different than any other method call.

    Again, this is not an entirely true statement.  I have tried many times to call a method that returns a Task<T>, and many times when I try to reference the Task<T>.Result property, the program dead locks.  If I "await" the same method, it works fine and I get the result I expect.  So apparently, regardless of all the "theory", the reality thus far has been that Task and async/await are inexorably bound to each other.

    I think the difficulty here is that you are explaining Tasks and async/await from the class model side of the equation, with little to no regard for the concepts of multi-threading.  Now, I've written my own thread pool managers long before Task or async/await ever existed. I created my own virtual memory managers and thread pools before .Net existed, working with Win32 API directly.  So when you describe something that is unmistakably multi-threading and then say "don't think about threads" that's a bit contradictory.

    My focus is:  I want to multi thread a process. Thus, I am thinking in terms of "Threads" because that's what I want to achieve. You don't start from, "I want to use a Task" and then suddenly realize, "Wow, it's multi threading now."  You start with, "I want to load, save, process, or execute this operation asynchronously".  From there you find "Thread, BackgroundWorker, async/await & Task", as methods to achieve this result.  But, then you need to learn how to use them.  The concept is one thing, the syntax and language specification is something else.  I'm coming from the concept (threads) and attempting to learn the syntax and language specification that uses them (Task/async/await).

    Now, having written many of these subsystems before I know they're a pain in the ass.  Just because i've used 16-bit segment/offset pointers in older programs don't mean I want to go backwards.  I'll just use "new" on objects and call it a day, even though I know that everything is a pointer.  So, yea I don't want to reinvent the wheel, when the .Net Thread pool manager is fantastic for my needs, but i do need to know how Task uses it. 

    The nice thing about the interface though is that you shouldn't care.

    Oh, yea you should.  I believe all programmers should care.  I should care because when I write an application I should know if it's going to be multi-threaded or not. I should be 100% aware that it is or isn't multi-threaded.  I should know that a section is being run asynchronously, because I have to protect against cross thread exceptions.  If it's all synchronous, then I don't.  But if I don't care, and I leave it to chance that the "system" (or interface) is handling it for me, that causes bugs down the line because of synchronization contexts not being handled correctly.  So I ALWAYS care about when and where I want to start (or more accurately use) a thread.

    When working with tasks you really need to stop trying to line them up with how threads work. While they generally rely on threads that is completely an implementation detail.

    Um, kind of burying the lead there.  That's a rather huge "implementation detail", especially when dealing with UI (as a blatant example), but also when dealing with data as well.  Knowing threads are in play and when they aren't is essential for writing thread safe code.  In a completely synchronous application, I don't have to use InvokeRequired or a lock() statement because nothing is being executed on a separate thread.  If I don't know, I would have to wrap every data access point in a lock() statement, or check for InvokeRequired in every UI interaction which is unnecessary overhead if I'm not multi-threading the application.  It isn't the compiler's job, or .Net's job to make sure my code is safe, it's my job to do that, and in order to do so, the programmer has to know if threads are involved.

    I think another good way of looking at this is with disk IO. Do you care about whether the data you just wrote is saved to disk immediately or buffered up and sent later? Provided the data is guaranteed saved you shouldn't care.

    Uh, Yea.  I do care.  When dealing with multiple autonomous executions in an enterprise level daily batch processing cycle, it is essential to know that a file has been saved, and the handle to that file has been closed.  Subsequent operations might fail because an exclusive handle to that file was denied due to some caching system deciding to say, "Well, we'll get it saved some time, but you don't really care when."  That doesn't fly, and I've had to write many workarounds to verify that a file was flushed, closed, and the handle released in order for the next operation - in the same program or in an another - to function correctly.

    Same thing with tasks. You don't care where they run, you just want them to run.

    Um, no.  I kinda want them to run when and where I execute them. Like when a parent tells their child to go to their room, they don't mean next Tuesday, they mean now, and they don't mean go to the basement.  The Task is an object, that is handled by the Task system.  So, when I say Task.Run() I expect it to start running immediately.  Now, if what I'm running is supposed to run synchronously, I expect that the next line of code won't be executed until that "operation" is complete, and maybe that is not what Task.Run() is for.  From what you've described, it appears the "Task" object and it's "scheduler" were designed to execute that task in a separate "thread" space.  Yet you've denied threads so much it seems like your saying that Task.Run() might choose to not employ a worker thread.  It shouldn't be choosing jack, that's the programmer's job.  Now which worker thread it runs on, that I don't care.  But there is the main process entry thread, and if I want to run something on a separate thread, I need to know and control that.  Hence, if Task.Run() allows the next line of code to execute regardless of whether the "Task's" operation is completed or not: that sounds like multi-threading to me. 

    Case and Point:

    void DoSomething() 
    {
      int i = 0;
      while (i < 10000) do 
      {
        i++;
        if (i % 10 = 0) Console.WriteLine(i);
      }
    }
    
    void DoSomething()
    {
      int i = 0;
      while (i < 10000) do
      {
         i++;
         Task.Run(() => 
         {
            if (i % 10 == 0) Console.WriteLine(i);
         });
      }
    }
    
    In the second example, if Task is using threads and not running synchronously, it could miss several expected outputs of "i", because of a race condition. 

    So your event handler returns and the UI moves on but quietly behind the scenes that task is still running. When it completes you want to do some more work. That is where either continuations (the old approach) or await comes in. The compiler rewrites the code to be called after the task completes. Remember the UI thread has already moved on.

    This is multi-threading.  There is no difference in what you've described there then calling BackgroundWorker.RunWorkerAsync() in the same UI event.  In either scenario, whether you are using BackgroundWorker, or await Task, from within the worker thread space, if you try to update a progress bar without a "form.Invoke()" you'll find out really quick this is multi-threaded.  You keep describing threading and then telling me to not think about threads.  I'm not handling the creation, destruction, or management of the threads, but I'm still using them, so I need to know when it's threading and when it isn't.  Hence the nature of the question to begin with.  With Thread and BackgroundWorker it is unmistakable when the code is being executed in a a different thread space.  The Syntax around Task (or async/await) has not been so clear.

    Now, it does appear that the "await" keyword was introduced to simplify the syntax around the continuations.  So that the compiler would handle the "Task is done" for the purposes of completing the synchronous UI event handler.  So within a click event, we chould run an operation, not block the UI window's message loop, and then do something with the result of that operation when it's completed.  That part makes sense.

    Again, stop thinking about this in terms of threads. But if I were to attempt to answer your question it is at the point you call Task.Run (because you pass it the "entry point"). In async/await code the compiler puts that at each "await" effectively. The `async` keyword on the method has no impact on the runtime behavior of the app.

    Well, I believe this actually does answer my question then.  That Task.Run() is the thread entry point, and the async/await keywords are there to provide syntactical reference for the programmer to know a method is in the async/await pattern, and for the compiler to provide some behind the scenes magic on the execution management within the calling method. Thus from earlier descriptions it seems, that the pattern is to use the Task.Run(this.MySyncMethod) to run the Synchronoous method in an asynchronous thread space.  If the Task needs to complete before the calling method continues, you would "await Task.Run(...)" in that method which also prevents the main thread form being blocked. 

    Hence i would expect (and proved) that the two methods bolew (OldWay/NewWay) are effectively synonymous:

            static void Main(string[] args)
            {
                Console.WriteLine($"Main Thread: {Thread.CurrentThread.ManagedThreadId}");
                OldWay();
                NewWay();
                Console.ReadKey();
            }
    
    
            static void OldWay()
            {
                bool done = false;
                BackgroundWorker bw = new BackgroundWorker();
                bw.DoWork += new DoWorkEventHandler((s, e) =>
                {
                    Console.WriteLine($"BW Thread: {Thread.CurrentThread.ManagedThreadId}");
                     int result = 0;
                     for (int i = 0; i < 100000; i++)
                         result += i;
                     e.Result = result;
                });
                bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler((s, e) =>
                {
                    Console.WriteLine($"BW: {e.Result}");
                    done = true;
                });
                bw.RunWorkerAsync();
                while (!done) Thread.Sleep(100);
                Console.WriteLine("BW Complete.");
            }
    
            static void NewWay()
            {
                Task<int> t = Task.Run<int>(() =>
                {
                    Console.WriteLine($"Task Thread: {Thread.CurrentThread.ManagedThreadId}");
                    int result = 0;
                    for (int i = 0; i < 100000; i++)
                        result += i;
                    return result;
                });
                t.Wait();
                Console.WriteLine($"Task: {t.Result}");
                Console.WriteLine("Task Complete.");
            }
    

    Thanks

    J"SD"al'RR


    "Never Trust a computer. Your brain is smarter than any micro-chip."
    PS - Don't mark answers on other people's questions. There are such things as Vacations and Holidays which may reduce timely activity, and until the person asking the question can test your answer, it is not correct just because you think it is. Marking it correct for them often stops other people from even reading the question and possibly providing the real "correct" answer.

    Thursday, November 14, 2019 4:21 PM
  • I think I've provided as much information as I can to help you out. I'm disconnecting from this thread. Hopefully someone else can better answer your questions.

    While you're waiting I recommend you google for how the task schedulers work in .NET, including the default scheduler, and the scenarios under which tasks run synchronously and the impact of using async. Perhaps this will provide more light to your questions.


    Michael Taylor http://www.michaeltaylorp3.net

    Thursday, November 14, 2019 5:05 PM
    Moderator
  • You did effectively answer it, while somewhat muddying the issues. 

    1. Tasks use Threads.
    2. Task.Run() is the entry to the thread
    3. async is an indicator that a method will await task operations.
    4. await is an indicator that the calling thread can continue while the calling method is awaiting the completion of the awaited Task.
    5. Returning Task or Task<T> from a method is there for multiple Task executions, that may or may not include child asynchronous operations, or synchronous operations within that Task.
    6. To execute a non-task method asynchronously, use the Task.Run(...) instead of making the method return Task if it isn't utilizing Tasks internally.

    So thank you for the help, and the discussion, i did get quite a bit of insight into the Task system.

    Thanks

    J"SD"a'RR


    "Never Trust a computer. Your brain is smarter than any micro-chip."
    PS - Don't mark answers on other people's questions. There are such things as Vacations and Holidays which may reduce timely activity, and until the person asking the question can test your answer, it is not correct just because you think it is. Marking it correct for them often stops other people from even reading the question and possibly providing the real "correct" answer.


    • Edited by JaedenRuiner Thursday, November 14, 2019 6:00 PM
    Thursday, November 14, 2019 5:57 PM