none
Regarding Nested Task RRS feed

Answers

  • I don't believe that is syntactically correct but the concept of a task calling a task makes sense and is, in fact, the requirement for proper async code. The term is generally "async all the way". That means that once you enter an async method everything from there down needs to be async. If it isn't then you can run into problems and/or are defeating the purpose. So you will almost always have async code awaiting on child tasks.

    public async Task LongRunningTask1Async ()
    {
       //Do async work here
    }
    
    public async Task LongRunningTask2Async ()
    {
       //Do async work here
    }
    
    async Task DoWork ()
    {
       await LongRunningTask1Async().ConfigureAwait(false);
       await LongRunningTask2Async().ConfigureAwait(false);
    }

    But your specific example doesn't make any sense. If you are calling Task.Run (which is not how you do async by the way) then you are already running on a task so calling Task.Run inside it again doesn't make sense. Just call method 1 followed by method 2. You gain nothing, in this case, by making the second block async.

    await Task.Run(() => {
       longrunningtask1();
    
       //No benefit in using async here, already on a task
       longrunningtask2();
    });

    Furthermore it is considered universally wrong to use Task.Run to wrap a sync method anyway. This can result in deadlocks if you're not careful. You can refer to the many blog articles on the topic. Task.Run should be used only in the rare cases you must have an async call AND the existing code cannot be converted to async (because you don't own it). In that case you need to test for all the environments that the code will run in to ensure that a deadlock doesn't occur.


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by Sudip_inn Thursday, July 9, 2020 2:52 PM
    Thursday, July 9, 2020 1:44 PM
    Moderator
  • By default when a task completes the runtime attempts to resume execution of the code after the task (e.g. `LongRunningTask2Async`) on the original thread. This is because there may be thread-specific stuff that needs to happen. The defacto example is calling an async method from a UI event handler and then wanting to update the UI. This has to happen on the UI thread (original thread). That is the default but often it doesn't matter. If you don't need to run on the same thread (e.g. you aren't using COM objects and you aren't updating a UI) then the thread everything runs on isn't relevant. That is what `ConfigureAwait` does. Specifying false tells the runtime that the code after the task returns can be run on any available thread. This helps prevent deadlocks because the runtime isn't waiting to run on the original thread and generally is faster for the same reason. Thus in most cases you will use `ConfigureAwait` on async calls. Again, the only time you wouldn't is if you are dealing with thread-specific stuff like UI updates or COM objects. In library code it is recommended you always use it.

    You can call `DoWOrk` like you would any other async method.

    //In a sync method that can block and wait for the results
    DoWork().Wait();
    
    //In a sync method but don't wait
    var task = DoWork();
    //Do other stuff
    //Eventually need to wait on it though
    task.Wait();
    
    //In an async method
    async Task FooAsync() 
    {
       //Do work
    
       await DoWork().ConfigureAwait(false);
    
       //Do more work
    }
    
    //In a UI event handler that isn't async
    //Note syntax is not generally allowed but a special case exists for UI handlers
    async void btnWork_Click ( object sender, EventArgs e )
    {
       //On UI thread, update UI
       ...
    
       //Do real work - notice no ConfigureAwait(false) because
       //the code after this needs to run on the UI thread
       await DoWork();
    
       //Back on UI thread because did not use CA(false)
       ...
    }

    Google for more info on ConfigureAwait and uses in UI. It is a common blog topic.


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by Sudip_inn Friday, July 10, 2020 11:15 AM
    Thursday, July 9, 2020 3:18 PM
    Moderator
  • 1) ConfigureAwait is a little complicated. Google for this method and you'll get blog articles on why we have it, why the default is what it is and the problems it can cause and prevent.

       a) By default always use ConfigureAwait(false) on any await call. The only exception is if, after the await returns you need to update a UI element directly or call a COM object. In those cases do not use ConfigureAwait at all. Refer to my examples of earlier.
       b) If you use ConfigureAwait(false) and then touch a UI element in the same method then the best case will be your app crashes immediately. More likely however your app will continue to run, the UI won't update and at some random point in the future the app crashes for no reason. This is the same behavior you'd see if you tried to update the UI on an arbitrary thread. Never use ConfigureAwait(false) in a method that must call into the UI.

    2) An app starts with a single thread plus a pool of threads in the thread pool (TP) set up by the runtime. When you start using tasks the default scheduler will use threads in the TP to run these tasks. So the tasks are running on arbitrary threads.

    Yes your understanding on the ConfigureAwait true vs false is correct. True means same thread and false means any available thread (generally the thread in the TP).


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by Sudip_inn Friday, July 10, 2020 6:34 PM
    Friday, July 10, 2020 1:19 PM
    Moderator

All replies

  • Hello,

    What exactly are you attempting to do rather than giving an abstract example?


    Please remember to mark the replies as answers if they help and unmarked them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.

    NuGet BaseConnectionLibrary for database connections.

    StackOverFlow
    profile for Karen Payne on Stack Exchange

    Thursday, July 9, 2020 1:24 PM
    Moderator
  • I don't believe that is syntactically correct but the concept of a task calling a task makes sense and is, in fact, the requirement for proper async code. The term is generally "async all the way". That means that once you enter an async method everything from there down needs to be async. If it isn't then you can run into problems and/or are defeating the purpose. So you will almost always have async code awaiting on child tasks.

    public async Task LongRunningTask1Async ()
    {
       //Do async work here
    }
    
    public async Task LongRunningTask2Async ()
    {
       //Do async work here
    }
    
    async Task DoWork ()
    {
       await LongRunningTask1Async().ConfigureAwait(false);
       await LongRunningTask2Async().ConfigureAwait(false);
    }

    But your specific example doesn't make any sense. If you are calling Task.Run (which is not how you do async by the way) then you are already running on a task so calling Task.Run inside it again doesn't make sense. Just call method 1 followed by method 2. You gain nothing, in this case, by making the second block async.

    await Task.Run(() => {
       longrunningtask1();
    
       //No benefit in using async here, already on a task
       longrunningtask2();
    });

    Furthermore it is considered universally wrong to use Task.Run to wrap a sync method anyway. This can result in deadlocks if you're not careful. You can refer to the many blog articles on the topic. Task.Run should be used only in the rare cases you must have an async call AND the existing code cannot be converted to async (because you don't own it). In that case you need to test for all the environments that the code will run in to ensure that a deadlock doesn't occur.


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by Sudip_inn Thursday, July 9, 2020 2:52 PM
    Thursday, July 9, 2020 1:44 PM
    Moderator
  • If they are really "long running" tasks, why do you want to await them? Could you ask this question with a real life scenario?

    www.cihanyakar.com

    Thursday, July 9, 2020 2:21 PM
  • thanks for the reply

    async Task DoWork ()
    {
       await LongRunningTask1Async().ConfigureAwait(false);
       await LongRunningTask2Async().ConfigureAwait(false);
    }

    1) if we do not use ConfigureAwait(false) or if we set ConfigureAwait(true) then what will happen ?

    just give me a brief idea the advantage of using ConfigureAwait(false)

    2) can i call DoWork() like this way? right now i am not before dev pc. so could not test the code.

    thanks

    Thursday, July 9, 2020 3:03 PM
  • By default when a task completes the runtime attempts to resume execution of the code after the task (e.g. `LongRunningTask2Async`) on the original thread. This is because there may be thread-specific stuff that needs to happen. The defacto example is calling an async method from a UI event handler and then wanting to update the UI. This has to happen on the UI thread (original thread). That is the default but often it doesn't matter. If you don't need to run on the same thread (e.g. you aren't using COM objects and you aren't updating a UI) then the thread everything runs on isn't relevant. That is what `ConfigureAwait` does. Specifying false tells the runtime that the code after the task returns can be run on any available thread. This helps prevent deadlocks because the runtime isn't waiting to run on the original thread and generally is faster for the same reason. Thus in most cases you will use `ConfigureAwait` on async calls. Again, the only time you wouldn't is if you are dealing with thread-specific stuff like UI updates or COM objects. In library code it is recommended you always use it.

    You can call `DoWOrk` like you would any other async method.

    //In a sync method that can block and wait for the results
    DoWork().Wait();
    
    //In a sync method but don't wait
    var task = DoWork();
    //Do other stuff
    //Eventually need to wait on it though
    task.Wait();
    
    //In an async method
    async Task FooAsync() 
    {
       //Do work
    
       await DoWork().ConfigureAwait(false);
    
       //Do more work
    }
    
    //In a UI event handler that isn't async
    //Note syntax is not generally allowed but a special case exists for UI handlers
    async void btnWork_Click ( object sender, EventArgs e )
    {
       //On UI thread, update UI
       ...
    
       //Do real work - notice no ConfigureAwait(false) because
       //the code after this needs to run on the UI thread
       await DoWork();
    
       //Back on UI thread because did not use CA(false)
       ...
    }

    Google for more info on ConfigureAwait and uses in UI. It is a common blog topic.


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by Sudip_inn Friday, July 10, 2020 11:15 AM
    Thursday, July 9, 2020 3:18 PM
    Moderator
  • ConfigureAwait --> Specifying false tells the runtime that the code after the task returns can be run on any available thread. 
    This helps prevent deadlocks because the runtime isn't waiting to run on the original thread 
    and generally is faster for the same reason. 

    Thus in most cases you will use `ConfigureAwait` on async calls. 
    Again, the only time you wouldn't is if you are dealing with thread-specific stuff like UI updates or COM objects. 

    1) Not very clear what you try to say. i assume you are saying not requite to use ConfigureAwait = false when dealing with UI update from thread. am i right?

    when updating UI then if i use ConfigureAwait = false then what worse happen ?

    2) normally a program start with one primary thread and when we use ConfigureAwait = false

    or ConfigureAwait = true then when task complete then control
    return back to main thread but if i use ConfigureAwait = false then control back to any available thread instead of main thread...am i right.

    please answer for my 2 points which i understand from your explanation. just tell me my understanding is right?

    Thanks

    Friday, July 10, 2020 11:22 AM
  • 1) ConfigureAwait is a little complicated. Google for this method and you'll get blog articles on why we have it, why the default is what it is and the problems it can cause and prevent.

       a) By default always use ConfigureAwait(false) on any await call. The only exception is if, after the await returns you need to update a UI element directly or call a COM object. In those cases do not use ConfigureAwait at all. Refer to my examples of earlier.
       b) If you use ConfigureAwait(false) and then touch a UI element in the same method then the best case will be your app crashes immediately. More likely however your app will continue to run, the UI won't update and at some random point in the future the app crashes for no reason. This is the same behavior you'd see if you tried to update the UI on an arbitrary thread. Never use ConfigureAwait(false) in a method that must call into the UI.

    2) An app starts with a single thread plus a pool of threads in the thread pool (TP) set up by the runtime. When you start using tasks the default scheduler will use threads in the TP to run these tasks. So the tasks are running on arbitrary threads.

    Yes your understanding on the ConfigureAwait true vs false is correct. True means same thread and false means any available thread (generally the thread in the TP).


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by Sudip_inn Friday, July 10, 2020 6:34 PM
    Friday, July 10, 2020 1:19 PM
    Moderator