0

I want to call step2 method after step 1 is finished. With this code below the methods step 1 and step 2 are executing in parallel. And of course I they need to be asynchronous so they won't block step 3 from executing.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace test
{
    internal class Program
    {
        static void Main(string[] args)
        {

            async void step1()
            {
                await Task.Run(() =>
                {
                    Thread.Sleep(2000);
                    Console.WriteLine("step 1");
                });

            }

            async void step2()
            {
                await Task.Run(() =>
                {
                    Thread.Sleep(5000);
                    Console.WriteLine("step 2");
                });

            }

            void step3()
            {
                Console.WriteLine("step 3");
            }

            step1();
            step2();
            step3();
            
            // the code below is not working but just and idea 
            // what I want to make. If it at all posible
            // step1().step2()
            // step3() 

            Console.ReadLine();
        }
    }
}

Any help would be greatly appreciated. Keep in mind I am beginner in C#!

Edit:

  1. I know that I can get the end result even with this code.

             void step1()
             {
                 Thread.Sleep(2000);
                 Console.WriteLine("step 1");
             }
             void step2()
             {
                 Thread.Sleep(5000);
                 Console.WriteLine("step 2");
             }
             void step3()
             {
                 Console.WriteLine("step 3");
             }
             step3();
             step1();
             step2();
    

even without async/await at all.

  1. The point of this question is to make small proof of concept application where even though the code is set up like this:

             step1();
             step2();
             step3();
    

where step3() is set last will execute first because has no delay and also step2() will have to wait for step1() to finish.

Is this at all possible with async/await in C#. I think this can be done with promises in javascript.

Happy Coconut
  • 973
  • 4
  • 14
  • 33
  • 2
    `await step1();` etc. And mark `Main` as `async`. – Roman Ryzhiy Dec 09 '22 at 16:15
  • 1
    https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming#avoid-async-void – GSerg Dec 09 '22 at 16:16
  • 1
    Its just testcode but don't use Thread.Sleep here use Task.Delay. A Task isn't a thread. – Ralf Dec 09 '22 at 16:18
  • *"where step3() is set last will execute first because has no delay"* - From what I understand, you're saying, "I want to call it last but run it first." I don't understand why you would want this. If you want to run it first, what is the problem with calling it first? – Gabriel Luci Dec 09 '22 at 17:24
  • Like I said this is just a proof of concept app. Sometimes where step1() is some external library you don't have the luxury of calling stop3() first. – Happy Coconut Dec 09 '22 at 17:29
  • In the original code step3() executes first and that is not the problem. The bigger problem is for step2() to wait for step1() – Happy Coconut Dec 09 '22 at 17:31
  • *"Sometimes where step1() is some external library you don't have the luxury of calling stop3() first."* - Why not? You are writing the code that calls step1() and step3(). You have the luxury of calling whichever one you want first. – Gabriel Luci Dec 09 '22 at 17:36
  • OK I might be wrong. Like I said I am beginner and trying to learn. However back to my question, is it possible to do it or not? – Happy Coconut Dec 09 '22 at 17:40

4 Answers4

3

There may be a misunderstanding about how asynchronous methods work. All async methods start running synchronously, just like any other method. The magic happens when await acts on a Task that is incomplete. At that point, the method returns a new incomplete Task to the calling method (or returns nothing if it's void, which is why you should avoid async void).

So if you call step1() first, then step1() will start executing first - there is no way around that. But when step1() hits await Task.Run(...) then it returns and the Main method continues executing. At that point, you can decide what to do. Do you want to wait until step1() completes or go do something else?

Here is what the code would look like if you want:

  1. step1() to start executing first.
  2. step2() to only start after step1() completes.
  3. step3() executes as soon as possible after step1() starts, but without waiting for step1() to complete.
static async Task Main(string[] args)
{
    async Task step1()
    {
        Console.WriteLine("step 1 starting");
        await Task.Run(() =>
        {
            Thread.Sleep(2000);
            Console.WriteLine("step 1 done");
        });
    }

    async Task step2()
    {
        Console.WriteLine("step 2 starting");
        await Task.Run(() =>
        {
            Thread.Sleep(5000);
            Console.WriteLine("step 2 done");
        });
    }

    void step3()
    {
        Console.WriteLine("step 3");
    }

    var step1task = step1();
    step3();
    await step1task;
    await step2();

    Console.ReadLine();
}

The output is:

step 1 starting
step 3
step 1 done
step 2 starting
step 2 done

If you want step3() to execute before step1() even starts executing, then you need to call step3() first - no way around it.

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
2

If you change your Main method (as well as step1 and step2) to async Task instead of void (or async void), you'll be able to await your methods.

static async Task Main(string[] args)
{
    async Task step1()
    {
        await Task.Run(() =>
        {
            Thread.Sleep(2000);
            Console.WriteLine("step 1");
        });

    }

    async Task step2()
    {
        await Task.Run(() =>
        {
            Thread.Sleep(5000);
            Console.WriteLine("step 2");
        });

    }

    void step3()
    {
        Console.WriteLine("step 3");
    }

    step3(); // You can put step3 here if you want it to run before step1 and step2.
    await step1();
    await step2();
    

    Console.ReadLine();
}

I suggest you check this Microsoft article to get a better understanding of async Task vs async void.

Batesias
  • 1,914
  • 1
  • 12
  • 22
  • Thanks for answer! However step 3 is executed last here and not first. It should be the first method that is executed. – Happy Coconut Dec 09 '22 at 16:21
  • Sorry I didn't catch that part. I made the edit with a comment :) – Batesias Dec 09 '22 at 16:28
  • Yes I can move step3 to the top and I can even not use any async/await for step1 and step2 just regular void and step 2 will be executed after step 1 for sure. But That was not the point of the question. That is why I put step3 as last. – Happy Coconut Dec 09 '22 at 16:39
  • 4
    Then you should make that clear in the question. If it comes with conditions (like can't use async there) then name them and also say why they are there. – Ralf Dec 09 '22 at 16:42
  • 2
    I'm a bit confused now. Can you update the question with all the requirements? – Batesias Dec 09 '22 at 16:48
0

If the constraints are

  1. step2 should start when step1 ends
  2. step1 and step2 should not block step3
  3. Calling code must be (e.g. no ContinueWith)

step1();
step2();
step3();

Then the answer is this is not possible.

Unless you have control of the code inside the steps, and do thread signalling to prevent step2 to start before step1 ends. Read about ManualResetEvent.

static void Main(string[] args)
{
    var mre = new ManualResetEvent(false);

    void step1()
    {
        Task.Run(() =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("step 1");
            mre.Set();
        });
    }
    void step2()
    {
        Task.Run(() =>
        {
            mre.WaitOne();
            Console.WriteLine("step 2");
        });
    }
    void step3()
    {
        Console.WriteLine("step 3");
    }

    step1();
    step2();
    step3();

    Console.ReadLine();
}
Stig
  • 1,974
  • 2
  • 23
  • 50
-2

Drop the async, it is not meaning what you think. Use ContinueWith

    Task step1()
    {
        return Task.Run(() =>
        {
            Thread.Sleep(2000);
            Console.WriteLine("step 1");
        });
    }

    Task step2()
    {
        return Task.Run(() =>
        {
            Thread.Sleep(5000);
            Console.WriteLine("step 2");
        });
    }

    void step3()
    {
        Console.WriteLine("step 3");
    }

    var task = step1()
        .ContinueWith(x => step2());
    step3();
    task.Wait();
Stig
  • 1,974
  • 2
  • 23
  • 50
  • There's no reason to use `.ContinueWith()` over `await`. And while using `.Wait()` here doesn't really make a difference, it's a bad idea to get into the habit of using it. There's no reason to recommend it here. – Gabriel Luci Dec 09 '22 at 16:49
  • Yes .Wait() here is important when I don't use await and don't have Console.ReadLine(); The Program would exist before Task is finished. – Stig Dec 09 '22 at 16:55
  • Disagree again, ContinueWith and await has two difference purposes. He want's to chain tasks, which has nothing to do with await. – Stig Dec 09 '22 at 16:56
  • But you don't need `.Wait()` to do that. Mark the `Main()` method as `async` and use `await task;`. – Gabriel Luci Dec 09 '22 at 16:58
  • Also `await step1(); await step2();` is equivalent to `step1().ContinueWith(x => step2());`. The whole point of `aync`/`await` is to be able to write asynchronous code in a simpler way. – Gabriel Luci Dec 09 '22 at 16:59
  • But you are missing the point, where step3(); is called last but should be executed in parallel (before step1 and step2 is done). Read the question – Stig Dec 09 '22 at 17:02
  • If you can do it with `.ContinueWith()`, then you can do it with `await`. But it's clear from the comments on the other answer that the OP hasn't specified all the requirements very well. – Gabriel Luci Dec 09 '22 at 17:04
  • Your comment would be true, it you didn't had the constrain of step3 should be call last and run in parallel. So again, read the question. My answer fits perfect. – Stig Dec 09 '22 at 17:57
  • Even the accepted answer starts step2 before step1 ends. – Stig Dec 09 '22 at 17:59
  • But the constraint to call step3 last is entirely manufactured. There is no real scenario where that would be necessary. – Gabriel Luci Dec 09 '22 at 18:01
  • You know the use case? – Stig Dec 09 '22 at 18:06
  • No, but neither does the OP – Gabriel Luci Dec 09 '22 at 18:17
  • Yes and no. The OP wanted to call the methods in order: 1 then 2 then 3. But even yours doesn't do that. `step2()` *appears in the code* before `step3()`, but still ends up getting *called* after. The answer I posted does exactly the same as yours, but uses `async`/`await` over `.ContinueWith()`, which [avoids a lot of problems that can come from using `.ContinueWith()`](https://stackoverflow.com/a/18982576/1202807). And also, there is almost never a reason to use `.Wait()` (or `.Result`), which [causes problems too](https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html). – Gabriel Luci Dec 09 '22 at 18:45
  • 2
    The OP is a self-admitted newbie. Sometimes it's better to point out the fault in the question rather than recommend something that might contribute to bad habits. – Gabriel Luci Dec 09 '22 at 18:47
  • Do we look and the same code (of mine)? Code order should be 1,2,3. Did that, check. 1 should end before 2 should start. Check. 1 and 2 should not block 3. Check. – Stig Dec 09 '22 at 18:54
  • *"Code order should be 1,2,3"* - It depends how you define "order". Should it *run* in that order, or just *appear in the code* in that order? Your code has them *appearing in that order*, but the just disguises the fact that they *run* in the order 1, 3, 2. – Gabriel Luci Dec 09 '22 at 19:30
  • I agree with your last comment – Stig Dec 09 '22 at 20:02