C# 并行运行两个异步任务并在 .NET 4.5 中收集结果

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12343081/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 22:57:32  来源:igfitidea点击:

Run two async tasks in parallel and collect results in .NET 4.5

c#asynchronoustask-parallel-library.net-4.5

提问by Simon_Weaver

I've been trying for a while to get something I thought would be simple working with .NET 4.5

我一直在尝试获得一些我认为使用 .NET 4.5 很简单的东西

I want to fire off two long running tasks at same time and collect the
results in in the best C# 4.5 (RTM) way

我想同时启动两个长时间运行的任务并
以最好的 C# 4.5 (RTM) 方式收集结果

The following works but I don't like it because:

以下工作,但我不喜欢它,因为:

  • I want Sleepto be an async method so it can awaitother methods
  • It just looks clumsy with Task.Run()
  • I don't think this is even using any new language features at all!
  • 我想Sleep成为一个异步方法,所以它可以是await其他方法
  • 它只是看起来很笨拙 Task.Run()
  • 我认为这根本没有使用任何新的语言功能!

Working code:

工作代码:

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Task.Run(() => Sleep(5000));    
    var task2 = Task.Run(() => Sleep(3000));

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for a total of " + totalSlept + " ms");
}

private static int Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    Console.WriteLine("Sleeping for " + ms + " FINISHED");
    return ms;
}

Non working code:

非工作代码:

Update: This actually works and is the correct way to do it, the only problem is the Thread.Sleep

更新:这实际上有效并且是正确的方法,唯一的问题是 Thread.Sleep

This code doesn't work because the call to Sleep(5000)immediately starts the task running so Sleep(1000)doesn't run until it has completed. This is true even though Sleepis asyncand I'm not using awaitor calling .Resulttoo soon.

此代码不起作用,因为调用Sleep(5000)立即启动正在运行的任务,因此Sleep(1000)在完成之前不会运行。即使Sleep是这样async,我也不会很快使用await或打电话.Result

I thought maybe there is a way to get a non-running Task<T>by calling an asyncmethod so I could then call Start()on the two tasks, but I can't figure out how to get a Task<T>from calling an async method.

我想也许有一种方法可以Task<T>通过调用一个async方法来获得非运行状态,这样我就可以调用Start()这两个任务,但我不知道如何Task<T>从调用异步方法中获取 a 。

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Sleep(5000);    // blocks
    var task2 = Sleep(1000);

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
}

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    return ms;
}

采纳答案by softveda

You should use Task.Delay instead of Sleep for async programming and then use Task.WhenAll to combine the task results. The tasks would run in parallel.

您应该使用 Task.Delay 而不是 Sleep 进行异步编程,然后使用 Task.WhenAll 来组合任务结果。这些任务将并行运行。

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

            Console.WriteLine("Starting");

            var task1 = Sleep(5000);
            var task2 = Sleep(3000);

            int[] result = await Task.WhenAll(task1, task2);

            Console.WriteLine("Slept for a total of " + result.Sum() + " ms");

        }

        private async static Task<int> Sleep(int ms)
        {
            Console.WriteLine("Sleeping for {0} at {1}", ms, Environment.TickCount);
            await Task.Delay(ms);
            Console.WriteLine("Sleeping for {0} finished at {1}", ms, Environment.TickCount);
            return ms;
        }
    }

回答by Richard

While your Sleepmethod is async, Thread.Sleepis not. The whole idea of async is to reuse a single thread, not to start multiple threads. Because you've blocked using a synchronous call to Thread.Sleep, it's not going to work.

虽然您的Sleep方法是异步的,但Thread.Sleep不是。异步的整个想法是重用单个线程,而不是启动多个线程。因为您已经使用对 Thread.Sleep 的同步调用进行了阻塞,所以它不会起作用。

I'm assuming that Thread.Sleepis a simplification of what you actually want to do. Can your actual implementation be coded as async methods?

我假设这Thread.Sleep是对您实际想要做的事情的简化。您的实际实现可以编码为异步方法吗?

If you do need to run multiple synchronous blocking calls, look elsewhere I think!

如果您确实需要运行多个同步阻塞调用,我认为请查看其他地方!

回答by Simon_Weaver

This article helped explain a lot of things. It's in FAQ style.

这篇文章帮助解释了很多事情。它是常见问题解答样式。

Async/Await FAQ

异步/等待常见问题

This part explains why Thread.Sleepruns on the same original thread - leading to my initial confusion.

这部分解释了为什么Thread.Sleep在同一个原始线程上运行 - 导致我最初的困惑。

Does the “async” keyword cause the invocation of a method to queue to the ThreadPool? To create a new thread? To launch a rocket ship to Mars?

No. No. And no. See the previous questions. The “async” keyword indicates to the compiler that “await” may be used inside of the method, such that the method may suspend at an await point and have its execution resumed asynchronously when the awaited instance completes. This is why the compiler issues a warning if there are no “awaits” inside of a method marked as “async”.

“async”关键字是否会导致调用方法排队到线程池?创建一个新线程?向火星发射火箭飞船?

不。不。不。请参阅前面的问题。“async”关键字向编译器指示可以在方法内部使用“await”,这样方法可以在等待点挂起,并在等待的实例完成时异步恢复其执行。这就是为什么如果标记为“async”的方法内部没有“await”,编译器会发出警告。

回答by Bart

async Task<int> LongTask1() { 
  ...
  return 0; 
}

async Task<int> LongTask2() { 
  ...
  return 1; 
}

...
{
   Task<int> t1 = LongTask1();
   Task<int> t2 = LongTask2();
   await Task.WhenAll(t1,t2);
   //now we have t1.Result and t2.Result
}

回答by asidis

To answer this point:

回答这个问题:

I want Sleep to be an async method so it can await other methods

我希望 Sleep 是一个异步方法,以便它可以等待其他方法

you can maybe rewrite the Sleepfunction like this:

你可以Sleep像这样重写函数:

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    var task = Task.Run(() => Thread.Sleep(ms));
    await task;
    Console.WriteLine("Sleeping for " + ms + "END");
    return ms;
}

static void Main(string[] args)
{
    Console.WriteLine("Starting");

    var task1 = Sleep(2000);
    var task2 = Sleep(1000);

    int totalSlept = task1.Result +task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
    Console.ReadKey();
}

running this code will output :

运行此代码将输出:

Starting
Sleeping for 2000
Sleeping for 1000
*(one second later)*
Sleeping for 1000END
*(one second later)*
Sleeping for 2000END
Slept for 3000 ms

回答by Arvis

It's weekendnow!

现在是周末

    public async void Go()
    {
        Console.WriteLine("Start fosterage...");

        var t1 = Sleep(5000, "Kevin");
        var t2 = Sleep(3000, "Jerry");
        var result = await Task.WhenAll(t1, t2);

        Console.WriteLine($"My precious spare time last for only {result.Max()}ms");
        Console.WriteLine("Press any key and take same beer...");
        Console.ReadKey();
    }

    private static async Task<int> Sleep(int ms, string name)
    {
            Console.WriteLine($"{name} going to sleep for {ms}ms :)");
            await Task.Delay(ms);
            Console.WriteLine("${name} waked up after {ms}ms :(";
            return ms;
    }