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

2
  • 4
    Replace your Thread.Sleep with await Task.Delay. Commented May 5, 2021 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. Commented May 5, 2021 at 13:36

2 Answers 2

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");
    }
}
Sign up to request clarification or add additional context in comments.

Comments

-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()?

3 Comments

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.
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.
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, which is specifically designed to perform a switch to the ThreadPool.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.