【问题标题】:Async Task not running in background?异步任务不在后台运行?
【发布时间】:2016-03-02 17:01:06
【问题描述】:

我在玩 Async 并在下面制作了一个示例程序。示例程序比较了同步和异步操作所花费的时间

考虑到任务在后台线程上并行运行,我预计异步操作将花费更少的时间。但是下面的程序在同步和异步操作上花费相同的时间。我想我遗漏了一些东西,但是我到处都得到以下示例来创建异步任务并运行它们

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncTest {
    class Program {

    private static long max = 100;

    static void Main(string[] args) {
        var startTime = DateTime.Now;
        Console.WriteLine("Start Process at");

        //Normal();    //takes about 20 secs
        MainAsync().Wait();     //takes about 20 secs

        Console.WriteLine($"Total Time Taken {DateTime.Now.Subtract(startTime).TotalMilliseconds} ms");
        Console.ReadKey();

    }

    static void Normal() {
        for (long i = 0; i <= max; ++i) {
            Thread.Sleep(200);
        }
    }

    static async Task MainAsync() {
        var runner = new Runner();

        for (int i = 0; i <= 100; ++i) {
            await runner.Run(i, max / 100);
        }
    }

}

public class Runner {
    public async Task Run(int idx, long max) {
        await Task.Run(() => {
            for (long i = 1; i <= max; ++i) {
                Console.WriteLine($"Task {idx} - {i}");
                Thread.Sleep(200);
            }
        });
    }
}

}

任何帮助我在这里缺少什么?

【问题讨论】:

  • 为什么你会认为在后台线程上运行的东西比在主线程上运行花费的时间更少?在这两种情况下,您都在等待它完成。
  • ? 20 秒的任务是在主线程还是其他线程上运行并不重要 - 仍然需要 20 秒...您在寻找 Parallel.For 吗?
  • 您仍然将一项任务作为一项任务运行。无论您在哪个线程上运行它。如果您将其拆分为单独的较小任务并一起运行,则可以加快速度。
  • 如果你想在其他线程中运行,你不应该使用 Wait 方法。在此方法中,您可以使用 async void 或 TPL。
  • @AlexeiLevenkov 我认为 Run 方法将是异步的,这意味着一旦我等待它,它将与其他类似调用的任务并行运行。我认为 await runner.Run(...) 应该创建多个线程,这显然不会发生,因为任务是按顺序运行的。

标签: c# multithreading asynchronous


【解决方案1】:

只有满足以下条件才能加快速度:

1) 拥有多个处理器

2) 并且可以拆分任务以使其在处理器上并行。

但在您的示例中,您只是在等待,没有什么可以并行化。所以没有加速。

【讨论】:

  • 不清楚这篇文章与相关代码的关系。此外,Sleep 可以完美地并行化 - 您可以在进程可以处理的情况下休眠尽可能多的线程而不会产生太大影响(有更好的方法,但是让 10 个线程并行休眠是完全可行的)。
【解决方案2】:

在我看来,您错误地使用.Wait() 启动MainAsync,这将阻塞直到任务完成。在MainAsync 内,您可能假设启动runner 一百次,但实际上您在for 循环的每次迭代中等待(通过使用await)完成runner.Run()。 在Runner 类的Run 方法中,您在等待的地方启动Task(通过使用await),直到它也完成。 您还可以使用同步的Thread.Sleep 调用,该调用应替换为await Task.Delay()

总而言之,您在任何地方都在使用await,这会导致等待任务直到它完成执行。

你可以做什么,例如正在创建那些Task 实例,启动它们,然后使用await 等待它们。

例如一个做“一些繁重工作”的方法:

static async Task DoHeavyWork(int idx, int max)
{
    for (long i = 1; i <= max; ++i)
    {
        Console.WriteLine($"Task {idx} - {i}");
        await Task.Delay(200);
    }
}

现在让我们“开始”它们,直到它们全部完成:

static void Main(string[] args)
{
    int max = 10;
    List<Task> tasks = new List<Task>();

    for (int i = 0; i <= max; ++i)
    {
        tasks.Add(DoHeavyWork(i, max));
    }

    Task.WaitAll(tasks.ToArray());
}

如果您在Task.WaitAll 设置断点,您将看到,您将在所有任务完成执行之前到达此断点(首先,这个完整场景背后的想法是什么;-))。

【讨论】:

    【解决方案3】:

    第一个问题是您仍然按顺序运行任务:

    for (int i = 0; i <= 100; ++i) {
        await runner.Run(i, max / 100);
    }
    

    您开始任务,等到它完成,然后才继续迭代。只需将其更改为:

    await Task.WhenAll(Enumerable.Range(0, 101).Select(i => runner.Run(i, max / 100)));
    

    第二个问题是使用同步Thread.Sleep 调用。将Thread.Sleep(200) 替换为await Task.Delay(200)。可以使用Thread.Sleep 来模拟一些工作,但当您只想等待时,切勿将其与 TPL (async-await) 一起使用。

    【讨论】:

    • 谢谢安德烈。这种方法对我有用。我需要更仔细地研究创建任务:)
    猜你喜欢
    • 2014-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-18
    • 1970-01-01
    • 2022-09-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多