2
public class Program
{
    public static void Main(string[] args)
    {
        Start();
        Console.ReadLine();
    }

    private static async Task Start()
    {
        var m1 = method1();
        var m2 = method2();

        await Task.WhenAll(m1, m2);
    }


    private static async Task method1()
    {
        Console.WriteLine("Method1 - Start");
        Thread.Sleep(1000);
        Console.WriteLine("Method1 - End");
    }
    private static async Task method2()
    {
        Console.WriteLine("Method2 - Start");
        Thread.Sleep(1000);
        Console.WriteLine("Method2 - End");
    }
}

The above code returning below out

Method1 - Start
Method1 - End
Method2 - Start
Method2 - End

I want an output like

Method1 - Start
Method2 - Start
Method1 - End
Method2 - End

how to achieve that basically how to run async methods in parallel

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
  • 4
    Replace your `Thread.Sleep` with `await Task.Delay`. – GSerg May 05 '21 at 11:24
  • 1
    If you want "true" parallelism, call the methods with Task.Run or on new threads. Async is not really about parallelism per se and the degree of parallelism heavily depends on the structure of the actual method. Just imagine if method1() does a long running code before the await Task.Delay, it will be synchronous and method2() won't start until that code finishes and hits the first await. – János Pánczél May 05 '21 at 13:36

2 Answers2

3

Option A - with Task.Delay

public class Program
{
    public static async Task Main()
    {
        await Start();
        Console.ReadLine();
    }

    private static async Task Start()
    {
        var m1 = method1();
        var m2 = method2();

        await Task.WhenAll(m1, m2);
    }

    private static async Task method1()
    {
        Console.WriteLine("Method1 - Start");
        await Task.Delay(1000);
        Console.WriteLine("Method1 - End");
    }

    private static async Task method2()
    {
        Console.WriteLine("Method2 - Start");
        await Task.Delay(1000);
        Console.WriteLine("Method2 - End");
    }
}

Option B - with Task.Run

public class Program
{
    public static async Task Main()
    {
        await Start();
        Console.ReadLine();
    }

    private static async Task Start()
    {
        var m1 = Task.Run(() => method1());
        var m2 = Task.Run(() => method2());

        await Task.WhenAll(m1, m2);
    }

    private static void method1()
    {
        Console.WriteLine("Method1 - Start");
        Thread.Sleep(1000);
        Console.WriteLine("Method1 - End");
    }

    private static void method2()
    {
        Console.WriteLine("Method2 - Start");
        Thread.Sleep(1000);
        Console.WriteLine("Method2 - End");
    }
}
Peter Csala
  • 17,736
  • 16
  • 35
  • 75
-1

Or you can use Task.Yield().

public class Program
{
    public static void Main(string[] args)
    {
        Start();
        Console.ReadLine();
    }

    private static async Task Start()
    {
        var m1 = method1();
        var m2 = method2();

        await Task.WhenAll(m1, m2);
    }


    private static async Task method1()
    {
        await Task.Yield();
        Console.WriteLine("Method1 - Start");
        Thread.Sleep(1000);
        Console.WriteLine("Method1 - End");
    }
    private static async Task method2()
    {
        await Task.Yield();
        Console.WriteLine("Method2 - Start");
        Thread.Sleep(1000);
        Console.WriteLine("Method2 - End");
    }
}

This one is a bit "more parallel" than using Task.Delay() because it immediately yields back an uncomplete task, but with delay the "asynchronicity" only happens when you reach that line of code. If you have long running sync code before the delay, that will delay the execution of subsequent methods (in this case method2).

Edit

a more detailed explanation on When would I use Task.Yield()?

  • The `Task.Yield` is not a reliable (platform agnostic) way to switch to the `ThreadPool`. It will do so only in the absence of an ambient `SynchronizationContext`, and only accidentally, because it is specifically designed for use in environments with a synchronization context installed. For this reason I consider this solution a hack, and I would suggest using instead the `Task.Run` method, which is a non context-aware mechanism, designed specifically for offloading work to the `ThreadPool`. – Theodor Zoulias May 05 '21 at 17:23
  • Using `Task.Delay` (and in fact, any awaited awaitable) has the same drawback with `SynchronizationContext` (though at least you can use `ConfigureAwait(false)`). Since DotNet Core, neither console app nor AspNet has `SynchronizationContext` attached, I would not consider it as hack, but I have to agree, caution is advised. – János Pánczél May 05 '21 at 20:26
  • Yeap, the `await Task.Delay(1)` is probably a worse version of this hack. If someone really wants to avoid the `Task.Run` for some reason, they could consider using a custom awaiter like noseratio's [`TaskSchedulerAwaiter`](https://gist.github.com/noseratio/5d2d5f2a0cbb71b7880ce731c3958e62#file-taskschedulerawaiter-cs-L33), which is specifically designed to perform a switch to the `ThreadPool`. – Theodor Zoulias May 05 '21 at 20:41