Task承载的操作需要被调度才能被执行,由于.NET默认采用基于线程池的调度器,所以Task默认在线程池线程中执行。但是有的操作并不适合使用线程池,比如我们在一个ASP.NET Core应用中承载了一些需要长时间执行的后台操作,由于线程池被用来处理HTTP请求,如果这些后台操作也使用线程池来调度,就会造成相互影响。在这种情况下,使用独立的一个或者多个线程来执行这些后台操作可能是一个更好的选择。
一、基于线程池的调度一、基于线程池的调度二、TaskCreationOptions.LongRunning三、换成异步操作呢?四、换种写法呢?五、调用Wait方法六、自定义TaskScheduler七、独立线程池
我们通过如下这个简单的程序来验证默认基于线程池的Task调度。我们调用Task类型的静态属性Factory返回一个TaskFactory对象,并调用其StartNew方法启动一个Task对象,这个Task指向的Run方法会在一个循环中调用Do方法。Do方法使用自选等待的方式模拟一段耗时2秒的操作,并在控制台输出当前线程的IsThreadPoolThread属性确定是否是线程池线程。
【资料图】
Task.Factory.StartNew(Run);Console.Read();void Run(){ while (true) { Do(); }}void Do(){ var end = DateTime.UtcNow.AddSeconds(2); SpinWait.SpinUntil(() => DateTimeOffset.UtcNow > end); var isThreadPoolThread = Thread.CurrentThread.IsThreadPoolThread; Console.WriteLine($"[{DateTimeOffset.Now}]Is thread pool thread: {isThreadPoolThread}");}
通过如下所示的输出结果,我们得到了答案:利用TaskFactory创建的Task在默认情况下确实是通过线程池的形式被调度的。
二、TaskCreationOptions.LongRunning很明显,上述Run方法是一个需要永久执行的LongRunning操作,并不适合使用线程池来执行,实际上TaskFactory在设计的时候就考虑到了这一点,我们利用它创建一个Task的时候可以指定对应的TaskCreationOptions选项,其中一个选项就是LongRuning。我们通过如下的方式修改了上面这段程序,在调用StartNew方法时指定了这个选项。
Task.Factory.StartNew(Run, TaskCreationOptions.LongRunning);Console.Read();void Run(){ while (true) { Do(); }}void Do(){ var end = DateTime.UtcNow.AddSeconds(2); SpinWait.SpinUntil(() => DateTimeOffset.UtcNow > end); var isThreadPoolThread = Thread.CurrentThread.IsThreadPoolThread; Console.WriteLine($"[{DateTimeOffset.Now}]Is thread pool thread: {isThreadPoolThread}");}
再次执行我们的程序就会通过如下的输出结果看到Do方法将不会在线程池线程中执行了。
三、换成异步操作呢?由于LongRunning操作经常会涉及IO操作,所以我们执行方法经常会写成异步的形式。如下所示的代码中,我们将Do方法替换成DoAsync,将2秒的自旋等待替换成Task.Delay。由于DoAsync写成了异步的形式,Run也换成对应的RunAsync。
Task.Factory.StartNew(RunAsync, TaskCreationOptions.LongRunning);Console.Read();async Task RunAsync(){ while (true) { await DoAsync(); }}async Task DoAsync(){ await Task.Delay(2000);var isThreadPoolThread = Thread.CurrentThread.IsThreadPoolThread; Console.WriteLine($"[{DateTimeOffset.Now}]Is thread pool thread: {isThreadPoolThread}");}
再次启动程序后,我们发现又切换成了线程池调度了。为什么会这样呢?其实很好理解,由于原来返回void的Run方法被替换成了返回Task的RunAsync,传入StartNew方法表示执行操作的委托类型从Action切换成了Func
有人说,上面我们使用的是一个方法来表示作为参数的委托对象,如果我们按照如下的方式使用基于async/await的Lambda表达式呢?实际上这样的Lambda表达式就是Func
Task.Factory.StartNew(async () => { while (true) await DoAsync();}, TaskCreationOptions.LongRunning);Console.Read();async Task DoAsync(){ await Task.Delay(2000); var isThreadPoolThread = Thread.CurrentThread.IsThreadPoolThread; Console.WriteLine($"[{DateTimeOffset.Now}]Is thread pool thread: {isThreadPoolThread}");}五、调用Wait方法
其实这个问题很好解决,按照如下的方式将DoAsync方法换成同步形式的Do,将基于await的等待替换成针对Wait方法的调用就可以了。我想当你接触Task的时候,就有很多人不断提醒你,谨慎使用Wait方法,因为它会阻塞当前线程。实际上对于我们的硬要用场景,调用Wait方法才是正确的选择,因为我们的初衷就是使用一个独立的线程以独占的方式来执行所需的操作。
Task.Factory.StartNew(() => { while (true) Do(); }, TaskCreationOptions.LongRunning);Console.Read();void Do(){ Task.Delay(2000).Wait(); var isThreadPoolThread = Thread.CurrentThread.IsThreadPoolThread; Console.WriteLine($"[{DateTimeOffset.Now}]Is thread pool thread: {isThreadPoolThread}");}六、自定义TaskScheduler
既然针对线程池的使用是“Task调度”导致的,那么我们自然可以通过重写TaskScheduler的方式来解决这个问题。如下这个自定义的DedicatedThreadTaskScheduler 会采用独立的线程来执行被调度的Task,线程的数量可以参数来指定。
internal sealed class DedicatedThreadTaskScheduler : TaskScheduler{ private readonly BlockingCollection_tasks = new(); private readonly Thread[] _threads; protected override IEnumerable ? GetScheduledTasks() => _tasks; protected override void QueueTask(Task task) => _tasks.Add(task); protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) => false; public DedicatedThreadTaskScheduler(int threadCount) { _threads = new Thread[threadCount]; for (int index = 0; index < threadCount; index++) { _threads[index] = new Thread(_ => { while (true) { TryExecuteTask(_tasks.Take()); } }); } Array.ForEach(_threads, it => it.Start()); }}
我们演示实例中Run/Do方法再次还原成如下所示的纯异步模式的RunAsync/DoAsync,并在调用StartNew方法的时候创建一个DedicatedThreadTaskScheduler对象作为最后一个参数。
Task.Factory.StartNew(RunAsync, CancellationToken.None, TaskCreationOptions.LongRunning, new DedicatedThreadTaskScheduler(1));Console.Read();async Task RunAsync(){ while (true) { await DoAsync(); }}async Task DoAsync(){ await Task.Delay(2000); var isThreadPoolThread = Thread.CurrentThread.IsThreadPoolThread; Console.WriteLine($"[{DateTimeOffset.Now}]Is thread pool thread: {isThreadPoolThread}");}
由于创建的Task将会使用指定的DedicatedThreadTaskScheduler 对象来调度,DoAsync方法自然就不会在线程池线程中执行了。
七、独立线程池.NET提供的线程池是一个全局共享的线程池,而我们定义的DedicatedThreadTaskScheduler相当于创建了一个独立的线程池,对象池的效果可以通过如下这个简单的程序展现出来。
Task.Factory.StartNew(()=> Task.WhenAll( Enumerable.Range(1,6).Select(it=>DoAsync(it))), CancellationToken.None, TaskCreationOptions.None, new DedicatedThreadTaskScheduler(2));async Task DoAsync(int index){ await Task.Yield(); Console.WriteLine($"[{DateTimeOffset.Now.ToString("hh:MM:ss")}]Task {index} is executed in thread {Environment.CurrentManagedThreadId}"); var endTime = DateTime.UtcNow.AddSeconds(4); SpinWait.SpinUntil(() => DateTime.UtcNow > endTime); await Task.Delay(1000);}Console.ReadLine();
如上面的代码片段所示,异步方法DoAsync利用自旋等待模拟了一段耗时4秒的操作,通过调用Task.Delay方法模拟了一段耗时1秒的IO操作。我们在其中输出了任务开始执行的时间和当前线程ID。在调用的StartNew方法中,我们调用这个DoAsync方法创建了6个Task,这些Task交给创建的DedicatedThreadTaskScheduler进行调度。我们为这个DedicatedThreadTaskScheduler指定的线程数量为2。从如下所示的输出结果可以看出,6个操作确实在两个线程中执行的。
上一篇:银华中证光伏产业ETF净值下跌2.72% 请保持关注 前沿热点
下一篇:最后一页
Task承载的操作需要被调度才能被执行,由于 NET默认采用基于线程池的调度器,所以Task默认在线程池线程中执
金融界基金05月29日讯银华中证光伏产业ETF基金05月26日下跌2 54%,现价1 036元,成交5018 19万元。当前本基
今天小编肥嘟来为大家解答以上的问题。罪全书7txt下载,十宗罪7txt下载全文相信很多小伙伴还不知道,现在让
1、《鱼》字笔画、笔顺汉字鱼(字典、组词)读音yú播放部首鱼笔画数8笔画撇、横撇 横钩、竖、横折、横、竖
贵州安顺黄果树瀑布天星洞出口,一小孩用砖头砸断钟乳石。目击者称,小孩拿着砖头敲了三次,自行带走砸下来
文 羊城晚报全媒体记者郭起图 受访者提供全国十大城管微博排名5月26日,由新浪微博出品、人民网舆情数据中
1、桃李芬芳仅从字面理解,就是桃树、李树的花或果实的香味。2、但引申的意思,一般指老师的学生很有成就。
热热热!今天的上海明显有了初夏的味道。在副热带高压控制下,申城今天热力四射,宝山、崇明、嘉定、青浦等
鸿博股份晚间公告,公司股票交易价格于2023年5月25日、5月26日连续两个交易日收盘价格涨幅偏离值累计超过20
长沙晚报掌上长沙5月28日讯据红网消息 5月28日16时,湖南省气象台发布高温黄色预警,这也是湖南今年发布的
C919大型客机顺利开启商业首航,意味着广大旅客终于能坐国产大飞机出行了。未来,飞行安全和服务如何保障?
今天来聊聊关于新天龙八部OL曼陀山庄会心好变态,新天龙八部ol的文章,现在就为大家来简单介绍下新天龙八部
建议在煮鸡蛋时加入适量的食用盐或者白醋,这样在煮制过程中,鸡蛋的蛋壳会软化,可以使煮好的鸡蛋更容易剥
今天小红来为大家带来的是molly盲盒多少钱一个,molly盲盒,让我们一起往下看看吧!1、其实盲盒这种东西多
撰文|田小梦编辑|李信马题图|ICPhoto5月24日,联想集团公布截至2023年3月31日的2022 23财年全年及第四财季
1、消费同时可以积分,但是你是ok会员卡还是ok积点卡?积点卡消费是没有积分的,你必须把积点卡里面的积点
沐浴炸弹为您的沐浴仪式锦上添花。也许您已经成功地为朋友和同事制作了沐浴露,但您想继续前进并开始销售您
1、国家安全生产监督管理局电工证查询系统网站为各省安全监督生产管理局网站查询。2、 特种作业操作证全
来源:新华社央视新闻环球网拜登和麦卡锡已就提高债务上限达成一致新华社快讯:美国总统拜登和国会众议院共
“豆腐,豆腐……”吉林桦甸横道河子乡,行走在乡间小路上,前方有辆小轿车,车顶绑着大喇叭,叫卖声从...
辽航警107 23,渤海海峡黄海北部,自5月28日1600时至6月4日1600时在38-51 7N121-38 2E、38-34 2N121-38 2E
5月26日,一则“”陕西省园丁中学校长被宝鸡市教育局长喊人打到大小便失禁住院”的消息引关注。奔流新闻...
一名男子驾驶奥迪违停,面对执勤交警时还大声训斥……近日网传的一段视频引发关注,不少网友称该男子为...
在昨夜今晨进行的两项决赛里,男双方面,王楚钦搭档樊振东,以3-0的比分横扫韩国组合,强势夺冠,这也是两
湖人谁去谁留?美媒列4人名单:2人地位不可撼动2人已成交易筹码,湖人,比斯利,里夫斯,丹佛掘金队,威廉·...
X 关闭
X 关闭