栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

是否可以保证Task.Factory.StartNew()使用调用线程以外的其他线程?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

是否可以保证Task.Factory.StartNew()使用调用线程以外的其他线程?

我就此问题邮寄了PFX小组成员Stephen Toub
。他很快就很详细地回到了我身边,所以我将在这里复制并粘贴他的文字。我还没有全部引用,因为阅读大量引用的文本最终会变得不如香草黑与白舒适,但实际上,这是斯蒂芬-
我不知道这么多东西:)我做了这个答案社区Wiki反映出以下所有优点并不是我真正的内容:

如果您调用已完成

Wait()
的Task,则不会有任何阻塞(如果任务以TaskStatus而不是来完成
RanToCompletion
,否则将抛出异常,否则将返回nop)。如果您调用
Wait()
已经执行的Task,则它必须阻塞,因为它无法合理地做其他事(当我说阻塞时,我同时包含了基于内核的真正的等待和自旋,因为它通常会同时混合两者)。同样,如果您调用状态
Wait()
Created
或的任务,则
WaitingForActivation
该任务将阻塞直到任务完成。这些都不是正在讨论的有趣案例。

有趣的情况是,当您

Wait()
在该
WaitingToRun
状态下调用Task时,这意味着该任务先前已排队到TaskScheduler中,但该TaskScheduler尚未真正开始运行Task的委托。在这种情况下,
Wait
通过调用调度程序的
TryExecuteTaskInline
方法,对的调用将询问调度程序是否可以在当前线程上运行该任务。这称为
内联
。调度程序可以选择通过调用来内联任务
base.TryExecuteTask
,也可以返回’false’表示它没有执行任务(通常使用类似…的逻辑来完成)。

return SomeSchedulerSpecificCondition() ? false : TryExecuteTask(task);

TryExecuteTask
返回布尔值的原因是它处理同步以确保给定Task仅执行一次。因此,如果调度程序希望在期间完全禁止内联该任务
Wait
,则可以将其实现为:
returnfalse;
如果调度程序希望始终尽可能允许内联,则可以将其实现为:

return TryExecuteTask(task);

在当前的实现中(.NET 4和.NET
4.5,并且我个人不希望这会改变),针对ThreadPool的默认调度程序允许内联,如果当前线程是ThreadPool线程,并且该线程是否是一个先前已将任务排队的人。

请注意,这里没有任意可重入性,因为默认的调度程序在等待任务时不会泵送任意线程…它只会允许该任务被内联,当然,任何内联该任务都会决定去做。另请注意,

Wait
在某些情况下甚至都不会询问调度程序,而宁愿阻塞。例如,如果您传递一个可取消的CancellationToken,或者您传递了一个非无限超时,则它不会尝试内联,因为内联任务的执行可能要花费任意长的时间,或者是全部或全部,最终可能会大大延迟取消请求或超时。总的来说,TPL试图在浪费正在做的线程之间取得平衡。
Wait
过多地重复使用该线程。这种内联对于递归的“分而治之”问题(例如QuickSort)非常重要,在该问题中您会生成多个任务,然后等待所有任务完成。如果这样做是在没有内联的情况下进行的,那么当您耗尽池中的所有线程以及它想要提供给您的任何将来线程时,您很快就会陷入僵局。

与分开

Wait
,也有可能(远程)结束Task.Factory.StartNew调用的执行,然后再执行此操作,前提是所使用的调度程序选择作为QueueTask调用的一部分同步运行任务。.NET中内置的任何调度程序都不会做到这一点,我个人认为对于调度程序来说这是一个糟糕的设计,但从理论上讲是可能的,例如:

protected override void QueueTask(Task task, bool wasPreviouslyQueued){    return TryExecuteTask(task);}

那的重载

Task.Factory.StartNew
不能接受
TaskScheduler
来自的使用调度器
TaskFactory
,在
Task.Factory
目标的情况下
TaskScheduler.Current
。这意味着,如果您
Task.Factory.StartNew
从排队到这个神话的Task内调用
RunSynchronouslyTaskScheduler
,它也将排队到
RunSynchronouslyTaskScheduler
,导致该
StartNew
调用同步执行Task。如果您对此非常担心(例如,您正在实现一个库,但您不知道从何处调用),则可以显式传递
TaskScheduler.Default
给该
StartNew
调用,使用
Task.Run
(总是转到
TaskScheduler.Default
),或使用
TaskFactory
创建的定位
TaskScheduler.Default


编辑:好的,看来我完全错了,并且当前正在等待任务的线程 可以 被劫持。这是发生这种情况的一个简单示例:

using System;using System.Threading;using System.Threading.Tasks;namespace ConsoleApplication1 {    class Program {        static void Main() { for (int i = 0; i < 10; i++) {     Task.Factory.StartNew(Launch).Wait(); }        }        static void Launch()        { Console.WriteLine("Launch thread: {0}",         Thread.CurrentThread.ManagedThreadId); Task.Factory.StartNew(Nested).Wait();        }        static void Nested()        { Console.WriteLine("Nested thread: {0}",         Thread.CurrentThread.ManagedThreadId);        }    }}

样本输出:

Launch thread: 3Nested thread: 3Launch thread: 3Nested thread: 3Launch thread: 3Nested thread: 3Launch thread: 3Nested thread: 3Launch thread: 4Nested thread: 4Launch thread: 4Nested thread: 4Launch thread: 4Nested thread: 4Launch thread: 4Nested thread: 4Launch thread: 4Nested thread: 4Launch thread: 4Nested thread: 4

如您所见,很多时候等待线程被重用于执行新任务。即使线程已获得锁,也会发生这种情况。讨厌的重新进入。我感到震惊和担心:(



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/432966.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号