这样在 C# 使用 LongRunnigTask 是错的

简介: Task.Factory.StartNew 有一个重载,是支持 TaskCreationOptions.LongRunning 参数来指定 Task 的特征的。但是可能在没有注意的情况下,你就使用了错误的用法。那么本文我们来简单阐述一下这个参数的作用,和使用的注意要点。

Task.Factory.StartNew 有一个重载,是支持 TaskCreationOptions.LongRunning 参数来指定 Task 的特征的。但是可能在没有注意的情况下,你就使用了错误的用法。那么本文我们来简单阐述一下这个参数的作用,和使用的注意要点。

这样其实是错误的

有的时候,你可能会这么写:

Task.Factory.StartNew(async () =>
{
    while (true)
    {
        // do something
        await Task.Delay(1000);
    }
}, TaskCreationOptions.LongRunning);

但其实,这是个错误的写法。

为什么需要 LongRunning

我们通常两种情况下会想到使用 TaskCreationOptions.LongRunning 参数:

  1. 你的任务需要长时间运行,比如一个循环,或者一个死循环。用来从队列中取数据,然后处理数据,或者是一些定时任务。
  2. 你的任务需要占用大量的 CPU 资源,是一个很大的循环,比如要遍历一个很大的数组,并做一些处理。

那么这个时候,我们就需要使用 TaskCreationOptions.LongRunning 参数来指定 Task。

因为我们可能学习到了,Task 默认的 Scheduler 是 ThreadPool,而 ThreadPool 的线程是有限的,如果你的任务需要长时间运行,或者是需要占用大量的 CPU 资源,那么就会导致 ThreadPool 的线程不够用。导致线程饥饿,或者是线程池的线程被占用,导致其他的任务无法执行。

于是我们很聪明的就想到了,我们可以使用 TaskCreationOptions.LongRunning 参数来指定 Task,这样就可以避免线程饥饿。

弄巧成拙

但是实际上,开篇的写法并不能达到我们的目的。

我们可以通过以下代码来验证一下:

var task = Task.Factory.StartNew(async () =>
{
    while (true)
    {
        // do something
        await Task.Delay(1000);
    }
}, TaskCreationOptions.LongRunning);
Thread.Sleep(3000);
Console.WriteLine($"Task Status: {task.Status}");
// Task Status: RanToCompletion

我们可以看到,Task 的状态是并非是 Running,而是 RanToCompletion。

也就是说,我们的任务在 3 秒后就已经执行完了,而不是我们想要的长时间运行。

究其原因,是因为我们采用了异步的方式来执行任务。而异步任务的执行,是通过 ThreadPool 来执行的。也就是说,虽然我们使用了 TaskCreationOptions.LongRunning 参数,来想办法指定线程池单独开一个线程,但是实际上在一个 await 之后,我们的任务还是在 ThreadPool 中执行的。

这会导致,我们的任务实际上后续又回到了 ThreadPool 中,而不是我们想要的单独的线程。起不到单独长期运行的作用。

正确的写法

因此,实际上如果想要保持单独的线程持续的运行,我们需要移除异步的方式,改为同步的方式。

var task = Task.Factory.StartNew(() =>
{
    while (true)
    {
        // do something
        Thread.Sleep(1000);
    }
}, TaskCreationOptions.LongRunning);
Thread.Sleep(3000);
Console.WriteLine($"Task Status: {task.Status}");
// Task Status: Running

这样我们就可以看到,Task 的状态是 Running,而不是 RanToCompletion。我们通过 TaskCreationOptions.LongRunning 参数,单独开启的线程就可以一直运行下去。

实际上还有很多考量

要考量 TaskScheduler 的实现

本文采用的是 aspnetcore 的实现,但是在其他的实现中,可能会有不同的实现。你也完全有可能实现一个 await 之后,不回到 ThreadPool 的实现。

LongRunning 也不是就不能用异步

正如开篇提到的第二种场景,如果你的业务是在第一个 await 之前有大量的同步代码,那么此时单独开启一个线程,也是有意义的。

我就是一个死循环,里面也是异步的怎么办

那么你可以考虑让这个 LongRuning 的 Task,不要 await,而是通过 Wait() 来等待。这样就可以避免 LongRunning 的 Task 直接结束。

总结

本文我们简单阐述了 TaskCreationOptions.LongRunning 参数的作用,和使用的注意要点。

参考

感谢您的阅读,如果您觉得本文有用,快点击下方点赞按钮👍,让更多的人看到本文。

欢迎关注作者的微信公众号“newbe技术专栏”,获取更多技术内容。


  1. https://www.cnblogs.com/eventhorizon/p/15912383.html
  2. https://threads.whuanle.cn/3.task/
  3. https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcreationoptions?view=net-7.0&WT.mc_id=DX-MVP-5003606
目录
相关文章
|
Windows
mathtype7产品激活密钥最新
MathType是强大的数学公式编辑器,MathType公式编辑器可以说是专门为理科生准备的软件,它可以帮助用户快速的在各种文档中插入符号和公式,不论是简单的公式和符号,还是复杂的都可以非常轻松的输入,并且在与office文档结合使用时,表现的非常完美,是非常好的一款软件,与常见的文字处理软件和演示程序配合使用,能够在各种文档中加入复杂的数学公式和符号,可用在编辑数学试卷、书籍、报刊、论文、幻灯演示等方面,是编辑数学资料的得力工具。
51946 0
|
新零售 前端开发 JavaScript
盒马跨端设计系统 ReX Design For OS
6 个月前,在 D2 前端技术论上我们向社区分享了《盒马中后台跨端方案》,详细介绍了我们在基于盒马实体零售数字化业务场景中,面向盒马营运数字化系统构建的跨端设计系统——ReX Design For OS。6 个月来,在开发资源极度紧张的状态下,我们持续建设和打磨,今天我们很高兴的告诉大家我们已经将项目的主要代码开源到了 Github,并发布了 beta 版本。
盒马跨端设计系统 ReX Design For OS
|
开发者 物联网 物联网安全
透视盒马:新零售操作系统的秘密
盒马是一个端到端,线上线下一体化的零售业务。在阿里CIO学院攻“疫”技术公益大咖说的第十六场直播中,盒马技术负责人何崚详细介绍了盒马产品技术在构建供给网络、销售网络、物流网络这三个核心命题时遇到的挑战和技术难点。
4806 2
|
C# 数据格式 XML
WPF 资源(StaticResource 静态资源、DynamicResource 动态资源、添加二进制资源、绑定资源树)
原文:WPF 资源(StaticResource 静态资源、DynamicResource 动态资源、添加二进制资源、绑定资源树) 一、WPF对象级(Window对象)资源的定义与查找 实例一: StaticR...
8957 0
halcon算子模板匹配(一)基于形状的模板匹配
halcon算子模板匹配(一)基于形状的模板匹配
4092 0
|
人工智能 API Apache
推荐3款开源、美观且免费的WinForm UI控件库
推荐3款开源、美观且免费的WinForm UI控件库
2301 6
|
缓存 算法 前端开发
如何降低 SPA 单页面应用的内存占用
单页面应用(SPA)由于其良好的用户体验而被广泛使用,但随着应用复杂度的增加,内存占用问题日益突出。本文将介绍几种有效降低SPA内存占用的方法,包括代码分割、懒加载、状态管理优化等技术,帮助开发者提升应用性能。
|
tengine 应用服务中间件 nginx
既然Tengine比Nginx更强大,为什么Tengine没有取代Nginx呢?
既然Tengine比Nginx更强大,为什么Tengine没有取代Nginx呢?
676 0
|
C#
蓝易云 - C#将异步改成同步方法
注意:虽然这样可以将异步方法转为同步,但在实际开发中,我们通常推荐使用异步方法,因为它可以提高应用程序的响应性和并发性。将异步方法转为同步可能会导致死锁或性能问题。
215 2