Task.Factory.StartNew 有一个重载,是反对 TaskCreationOptions.LongRunning 参数来指定 Task 的特色的。然而可能在没有留神的状况下,你就应用了谬误的用法。那么本文咱们来简略论述一下这个参数的作用,和应用的留神要点。
这样其实是谬误的
有的时候,你可能会这么写:
Task.Factory.StartNew(async () =>
{while (true)
{
// do something
await Task.Delay(1000);
}
}, TaskCreationOptions.LongRunning);
但其实,这是个谬误的写法。
为什么须要 LongRunning
咱们通常两种状况下会想到应用 TaskCreationOptions.LongRunning 参数:
- 你的工作须要长时间运行,比方一个循环,或者一个死循环。用来从队列中取数据,而后解决数据,或者是一些定时工作。
- 你的工作须要占用大量的 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 参数的作用,和应用的留神要点。
参考
- .NET Task 揭秘(2):Task 的回调执行与 await1
- Task2
- TaskCreationOptions3
感谢您的浏览,如果您感觉本文有用,快点击下方点赞按钮👍,让更多的人看到本文。
欢送关注作者的微信公众号“newbe 技术专栏”,获取更多技术内容。
- 本文作者:newbe36524
- 本文链接:https://www.newbe.pro/Others/0x026-This-is-the-wrong-way-to-use-LongRunnigTask-in-csharp/
- 版权申明:本博客所有文章除特地申明外,均采纳 BY-NC-SA 许可协定。转载请注明出处!
- https://www.cnblogs.com/eventhorizon/p/15912383.html ↩
- https://threads.whuanle.cn/3.task/ ↩
- https://learn.microsoft.com/en-us/dotnet/api/system.threading… ↩