【问题标题】:Why are parallel tasks not running?为什么并行任务不运行?
【发布时间】:2020-04-22 02:40:52
【问题描述】:

我有这段代码,它是为了证明问题而剥离的更大功能的骨架:

        var tasks = Enumerable.Range(0, 10)
            .Select(laneNo => Task.Run(() => Console.WriteLine($"Starting generator for lane {laneNo}")));

        for(int r=0; ;++r)
        {
            Task.Delay(TimeSpan.FromSeconds(3)).Wait();

            Console.WriteLine($"Iteration {r} at {DateTime.Now}");
        }

我从来没有看到“启动生成器”打印到控制台,但我确实看到迭代每 3 秒触发一次 - 某些原因导致这些任务无法进行(在实际代码中它们运行了很长一段时间,但删除它不会影响问题)。

为什么第一批任务没有运行?我的理论是它与Task.Delay有关吗?

【问题讨论】:

  • (顺便说一下,这是一个测试应用程序/模拟器的快速'n'dirty代码)
  • 如果您只是想并行运行一些同步代码,只需使用 Parallel.ForEach 即可。
  • 您在代码的哪个位置显示并行任务?您的 for 循环只是按顺序显示任务。你的任务可能在你进入 for 循环之前就终止了。
  • @jdweng 他们根本没有运行。在真正的代码中,他们会做更长的事情,但由于他们甚至不打印到控制台,所以出了点问题
  • 另外@jdweng 它仍然是并行的。如果您以这种方式运行“显示任务”,通常会看到它们无序打印。

标签: c# task-parallel-library


【解决方案1】:

您的 linq-statment 永远不会实现。 Linq 运算符(如 Select、Where、OrderBy 等)作为构建块将它们链接在一起,但直到您通过 foreach 运行它或使用不返回可枚举的运算符(如 ToArray、ToList、First、Last 等)时才会执行它们。

如果您在最后调用 ToList,您应该会看到所有正在执行的任务,但如果您只调用 First,您应该只会看到一个,因为原始 Range 的迭代将在第一个元素之后终止。

【讨论】:

    【解决方案2】:

    LINQ Select 已延迟执行;它只是定义了一个迭代器,因此不会生成您的 Tasks。

    您可以使用Task.WhenAll(IEnumerable<Task>),它将迭代并等待每个Task,生成新的Task,并在所有提供的任务也完成后完成:

    var tasks = Enumerable.Range(0, 10)
            .Select(laneNo => Task.Run(() => Console.WriteLine($"Starting generator for lane {laneNo}")));
    
    await Task.WhenAll(tasks);
    

    【讨论】:

    • 我不知道我会想出这个,我根本没有朝那个方向看!我以为我正在保存一行代码,而不是 Task.Run,而不是 new Task,然后在循环中使用 Starting。我从来没有意识到这些都是以这种方式懒惰地评估的:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多