【问题标题】:How to call multiple methods parallelly by using async programming in C#如何在 C# 中使用异步编程并行调用多个方法
【发布时间】:2021-05-05 11:18:30
【问题描述】:
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");
    }
}

上面的代码在下面返回

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

我想要这样的输出

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

如何实现,基本上如何并行运行异步方法

【问题讨论】:

  • await Task.Delay替换你的Thread.Sleep
  • 如果您想要“真正的”并行性,请使用 Task.Run 或在新线程上调用方法。异步本身并不是真正的并行性,并行度在很大程度上取决于实际方法的结构。想象一下,如果 method1() 在 await Task.Delay 之前执行了一个长时间运行的代码,它将是同步的,并且 method2() 将在该代码完成并遇到第一个 await 之前启动。

标签: c# asynchronous parallel-processing


【解决方案1】:

选项 A - 使用 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");
    }
}

选项 B - 使用 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");
    }
}

【讨论】:

    【解决方案2】:

    或者你可以使用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");
        }
    }
    

    这比使用Task.Delay() 更“并行”一点,因为它会立即返回一个未完成的任务,但随着延迟,“异步”仅在您到达那行代码时才会发生。如果你在延迟之前有长时间运行同步代码,那将延迟后续方法的执行(在这种情况下是方法2)。

    编辑

    关于When would I use Task.Yield()?的更详细解释

    【讨论】:

    • Task.Yield 不是切换到ThreadPool 的可靠(平台无关)方式。它只会在没有环境 SynchronizationContext 的情况下这样做,而且只是偶然的,因为它是专门为在安装了同步上下文的环境中使用而设计的。出于这个原因,我认为这个解决方案是一种 hack,我建议改用 Task.Run 方法,这是一种非上下文感知机制,专门用于将工作卸载到 ThreadPool
    • 使用Task.Delay(事实上,任何等待的等待)与SynchronizationContext有同样的缺点(尽管至少你可以使用ConfigureAwait(false))。由于 DotNet Core,无论是控制台应用程序还是 AspNet 都没有附加 SynchronizationContext,我不会认为它是 hack,但我必须同意,建议谨慎。
    • 是的,await Task.Delay(1) 可能是这个 hack 的更糟糕的版本。如果有人出于某种原因真的想避免使用Task.Run,他们可以考虑使用像noseratio 的TaskSchedulerAwaiter 这样的自定义等待器,它专门用于执行到ThreadPool 的切换。
    猜你喜欢
    • 2019-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-09
    • 2020-01-15
    • 2012-02-09
    • 1970-01-01
    相关资源
    最近更新 更多