【问题标题】:Not sure if code runs in parallel. Tasks in console application不确定代码是否并行运行。控制台应用程序中的任务
【发布时间】:2020-04-29 11:14:37
【问题描述】:

我有这个代码:

var dt = new DeveloperTest();
    var tasks = readers.Select(dt.ProcessReaderAsync).ToList();
    var printCounterTask = new Task(() => dt.DelayedPrint(output));
    printCounterTask.Start();

    Task.WhenAll(tasks).ContinueWith(x => dt.Print(output).ContinueWith(_ =>
    {
        dt.Finished = true;
    })).Wait();

    printCounterTask.Wait();

这样做是准备将要运行的任务,然后开始(我认为)并行执行,该执行以以下方式开始:

 printCounterTask.Start();

这就是延迟打印的作用:

public async Task DelayedPrint(IOutputResult output)
        {
            while (true)
            {
                if (!Finished)
                {
                    //every 10 seconds should print. 
                    //at least one print even if the execution is less than 10 seconds
                    //as this starts in paralel with the processing
                    Task.Delay(10 * 1000).Wait();
                    await Print(output);
                }
                else
                {
#if DEBUG
                    Console.WriteLine("Finished with printing");
#endif
                    break;
                }
            }
        }

基本上是每 10 秒打印一些延迟的输出,然后当所有任务完成时停止无限循环。 如果你想看完整的代码在这里https://github.com/velchev/Exclaimer-Test

我不确定这是不是

Task.WhenAll(tasks).ContinueWith(x => dt.Print(output).ContinueWith(_ =>
        {
            dt.Finished = true;
        })).Wait();

与 printCounterTask.Start() 并行运行; 当我首次亮相时,它似乎作为 !Finished 代码中的断点被命中,然后也在 else 子句中。据我所知,当您启动一项任务时,它会并行运行,因此所有任务都应该并行运行。任务是线程的表示,与旧语法相比,它在语法上更容易控制。所以所有这些线程都在运行,并且由于更好的语法更容易说 - 等到所有完成然后更改标志。任何有用的解释将不胜感激。谢谢小伙伴们。

【问题讨论】:

  • 能否将ProcessReaderAsync方法的代码包含进来?
  • 你正在通过Task.Delay().Wait() 阻塞 ThreadPool 中的所有线程。如果所有任务都应该并行运行,请将其更改为 await Task.Delay()
  • @SebastianSchumann Task.Delay().Wait() 是执行一项任务的一部分,printCounterTask。问题中未显示tasks 的实现(指向外部站点的链接不计入恕我直言)。
  • @TheodorZoulias 哦 - 是的。错过了。对不起。但 OP 应该改变这一点。
  • 如果您使用use Task.Run instead of new Task and Task.Startuse await instead of ContinueWith,您的代码将大大简化并且更加清晰。

标签: c# multithreading parallel-processing task


【解决方案1】:

编写的代码大部分是正确的,但是Task 构造函数和ContinueWith 周围存在一些细微差别,使其难以理解,并且易于破解。例如,printCounterTask.Wait() 不会等到DelayedPrint 完成,因为Task 构造函数不理解异步委托。

为了使代码完全正确并且更易于阅读和推理,请将new Task/Start 替换为Task.Run,并将ContinueWith 替换为await

var dt = new DeveloperTest();
var tasks = readers.Select(dt.ProcessReaderAsync).ToList();
var printCounterTask = Task.Run(() => dt.DelayedPrint(output));

await Task.WhenAll(tasks);
await dt.Print(output);
dt.Finished = true;

await printCounterTask;

如果您关注convention of suffixing asynchronous methods with Async,您还会发现您的代码更清晰。

任务是线程的表示,与旧语法相比,它在语法上更容易控制。

不,一点也不。任务是Future - 表示可能在未来某个时间完成的操作。这个“操作”does not necessarily require a threadTask.Run 为线程池做队列工作,但在这个例子中,任务并不总是使用线程池线程(具体来说,它在await Task.Delay 期间不使用线程池线程)。

【讨论】:

  • 谢谢我同意语法更好,等待不知道为什么我使用其他方式。同样,线程和任务是非常不同的概念,您可以使用使用线程池中的线程的任务启动异步操作,并且有一种方法可以使用语法知道它何时完成。 (供参考c-sharpcorner.com/article/task-and-thread-in-c-sharp)你会说await Task.WhenAll(tasks);没有并行运行,而 printCounterTask 正在执行?对我来说,它确实并行运行或至少异步运行......
  • 任务同时运行,是的。 await Task.WhenAll 实际上并没有任何事情;这只是(异​​步)等待任务的一点。 awaitWhenAll 实际上都不会导致额外的任务运行;它们只是“加入”点。
  • 不确定为什么有人甚至将我的问题投票给 -1....调用直到运行结束,每 10 秒的输出要求失败。”在测试和调试时,我认为这是不正确的,但无法从面试官那里得到更多信息。
  • 他们可能因为Task构造函数和ContinueWith而感到困惑?
  • 不知道...不应该是这种情况,因为他们似乎对所有级别的软件开发人员都使用相同的测试,这意味着他们非常熟悉问题和可能的解决方案。
【解决方案2】:

你说对了一部分。 这些任务将与

并行运行

printCounterTask

正如预期的那样。 然而,任务不是线程的表示,也不是更容易控制线程的语法糖化。 在这里您可以找到有用的信息: https://www.dotnetforall.com/difference-task-and-thread/

一般来说,了解任务使用线程池中的线程很重要。

任务是您希望作为后台工作执行的方法的表示(您不想阻塞当前执行),并且任务需要线程才能操作,但任务并非如此是一个线程。

线程池中的任务可能比可用线程多,这将导致它们在队列中等待可用线程以便执行。

还要考虑到 Task.WhenAll 不会为您执行任务,您必须自己执行它们(缺少 ProcessReaderAsync 的实现,但如果您使用的是 Task.Run 就可以了)。

【讨论】:

    猜你喜欢
    • 2013-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-16
    • 1970-01-01
    • 1970-01-01
    • 2015-09-27
    相关资源
    最近更新 更多