C# Task.Factory.StartNew Longrunning

3
我想了解为什么需要阻止以下内容才能使控制台输出:
Task.Factory.StartNew(() => Console.WriteLine("KO"), TaskCreationOptions.LongRunning);

而这个则不行:

new Thread(() => Console.WriteLine("KO")).Start();

根据《C# 5.0权威指南》,TaskCreationOptions.LongRunning可以使任务不使用池化线程(即后台线程),这意味着它应该使用前台线程,就像普通线程一样。但是对于普通线程,不需要Console.ReadlineWait(),而对于Task,无论是否长时间运行,我总是必须以某种方式阻塞主线程。
那么,LongRunningOnComplete()GetAwaiter()GetResult()或其他任何用于生成结果的函数有什么好处呢?如果我总是必须自己阻塞主线程才能获取结果,那么这些函数有什么作用呢?

“意思是它应该使用前台线程” [不,它不应该这样做。] (https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/ThreadPoolTaskScheduler.cs,61) - user4003407
1个回答

3

您正在依赖未定义的行为。不要这样做。

您不需要等待任务才能使其工作 - 这只是确保它以某种方式实际完成的唯一方法。我假设您只是使用一个仅包含上述代码的控制台应用程序 - 当线程实际到达 Console.WriteLine 部分时,主线程已经死亡,并且所有后台线程也都死亡了。 new Thread 默认创建前台线程,这可以防止整个应用程序退出,尽管“主”线程已终止。

任务(以及任何类型的异步操作)背后的想法是,它们允许您进行并发请求,并构建异步操作链(使它们表现得像同步操作,这通常是您想要的)。但是,您仍然需要同步点来实际制作可用的应用程序 - 如果应用程序在任务完成之前退出,那就太糟糕了 :)

如果您只是使用 Console.ReadLine 而不是显式等待任务完成,则可以看到此情况 - 它仍在后台运行,独立于执行的主线程,但现在您给它足够的时间来完成。在大多数应用程序中,您会将异步操作异步执行 - 例如,按钮单击的结果可能是异步 HTTP 请求,它不会阻塞 UI,但如果关闭 UI,则请求仍将终止。


嗨Luaan,感谢你澄清这个问题。我只是认为“LongRunning”应该将任务从后台线程转移到前台线程,并像普通线程一样防止应用程序退出。但是我理解你所说的应用程序在任务之前退出的情况,我永远不会知道结果,所以最好的方法是显式地阻塞线程,直到我得到任何结果。我还以为OnCompleted()应该为我做到这一点,所以当它没有时,我认为有些问题。 - AndrewH
@AndrewH 这只是向任务调度程序发出的提示,表明它不是纯CPU任务 - 这通常意味着它没有从线程池中分配一个线程。但新线程仍然是后台线程 - 这与线程池线程完全无关。任何线程都可以是前台或后台 - 前台线程会阻止应用程序退出,而后台线程则不会。但理想情况下,您希望清楚地了解您的线程正在做什么,并且只有在所有工作完成时才退出应用程序。你喜欢那些没有UI界面但却不会死的应用程序吗?这是一个常见的原因 :) - Luaan
感谢你的帮助,Luaan!非常感激。 - AndrewH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接