Ser*_*nko 155 c# multithreading task-parallel-library
我有方法:
private static void Method()
{
Console.WriteLine("Method() started");
for (var i = 0; i < 20; i++)
{
Console.WriteLine("Method() Counter = " + i);
Thread.Sleep(500);
}
Console.WriteLine("Method() finished");
}
Run Code Online (Sandbox Code Playgroud)
我想在一个新的Task中启动这个方法.我可以开始这样的新任务
var task = Task.Factory.StartNew(new Action(Method));
Run Code Online (Sandbox Code Playgroud)
或这个
var task = Task.Run(new Action(Method));
Run Code Online (Sandbox Code Playgroud)
但是Task.Run()
和之间有什么区别吗Task.Factory.StartNew()
?它们都使用ThreadPool并在创建Task的实例后立即启动Method().当我们应该使用第一个变体和第二个?
Chr*_*tos 166
第二种方法Task.Run
已经在.NET框架的更高版本中引入(在.NET 4.5中).
但是,第一种方法Task.Factory.StartNew
使您有机会定义有关要创建的线程的许多有用信息,Task.Run
而不提供此信息.
例如,假设您要创建一个长时间运行的任务线程.如果线程池的线程将用于此任务,则可以将此视为滥用线程池.
为避免这种情况,您可以做的一件事是在单独的线程中运行任务.一个新创建的线程,专门用于此任务,一旦您的任务完成就会被销毁.你无法实现这一点Task.Run
,而你可以这样做Task.Factory.StartNew
,如下所示:
Task.Factory.StartNew(..., TaskCreationOptions.LongRunning);
Run Code Online (Sandbox Code Playgroud)
正如这里所说:
因此,在.NET Framework 4.5 Developer Preview中,我们引入了新的Task.Run方法. 这绝不会废弃 Task.Factory.StartNew, 而应简单地将其视为使用 Task.Factory.StartNew 的快速方法,而无需指定一堆参数.这是捷径. 实际上,Task.Run实际上是根据用于Task.Factory.StartNew的相同逻辑实现的,只是传入一些默认参数.将Action传递给Task.Run时:
Task.Run(someAction);
Run Code Online (Sandbox Code Playgroud)
这完全等同于:
Task.Factory.StartNew(someAction,
CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
Run Code Online (Sandbox Code Playgroud)
Sco*_*ain 25
请参阅此博客文章,其中介绍了差异.基本上做:
Task.Run(A)
Run Code Online (Sandbox Code Playgroud)
和做的一样:
Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
Run Code Online (Sandbox Code Playgroud)
Zei*_*kki 22
这Task.Run
是在较新的.NET框架版本中引入的,建议使用.
从.NET Framework 4.5开始,Task.Run方法是启动计算绑定任务的推荐方法.仅当您需要对长时间运行的计算绑定任务进行细粒度控制时,才使用StartNew方法.
将Task.Factory.StartNew
有更多的选择,Task.Run
是一个简写:
Run方法提供了一组重载,可以使用默认值轻松启动任务.它是StartNew重载的轻量级替代品.
简而言之,我的意思是技术捷径:
public static Task Run(Action action)
{
return Task.InternalStartNew(null, action, null, default(CancellationToken), TaskScheduler.Default,
TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackMark);
}
Run Code Online (Sandbox Code Playgroud)
use*_*167 18
根据Stephen Cleary的这篇文章,Task.Factory.StartNew()很危险:
我在博客和SO问题中看到了很多代码,这些代码使用Task.Factory.StartNew来开始后台线程的工作.Stephen Toub有一篇很棒的博客文章,解释了为什么Task.Run比Task.Factory.StartNew好,但我想很多人都没有读过它(或者不理解它).所以,我采取了相同的论点,添加了一些更有力的语言,我们将看到这是怎么回事.:) StartNew确实提供了比Task.Run更多的选项,但是它非常危险,正如我们所看到的.您应该在异步代码中优先于Task.Run而不是Task.Factory.StartNew.
以下是实际原因:
- 不了解异步代理.这实际上与您想要使用StartNew的原因中的第1点相同.问题是,当您将异步委托传递给StartNew时,很自然地假设返回的任务代表该委托.但是,由于StartNew不了解异步委托,因此该任务实际代表的只是该委托的开头.这是编码器在异步代码中使用StartNew时遇到的第一个陷阱之一.
- 令人困惑的默认调度程序.好的,欺骗提问时间:在下面的代码中,方法"A"运行的是什么线程?
Task.Factory.StartNew(A);
private static void A() { }
Run Code Online (Sandbox Code Playgroud)
嗯,你知道这是一个棘手的问题,嗯?如果您回答"线程池线程",我很抱歉,但这不正确."A"将在TaskScheduler当前正在执行的任何内容上运行!
因此,这意味着如果操作完成,它可能会在UI线程上运行,并且由于Stephen Cleary在他的帖子中更充分地解释,因此会继续编组回UI线程.
就我而言,我在为视图加载数据网格时尝试在后台运行任务,同时还显示繁忙的动画.使用时不显示繁忙的动画,Task.Factory.StartNew()
但切换到时动画显示正常Task.Run()
.
有关详细信息,请参阅https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html
Myk*_*ych 16
人们已经提到
Task.Run(A);
Run Code Online (Sandbox Code Playgroud)
相当于
Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
Run Code Online (Sandbox Code Playgroud)
但是没有人提到
Task.Factory.StartNew(A);
Run Code Online (Sandbox Code Playgroud)
等效于:
Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Current);
Run Code Online (Sandbox Code Playgroud)
如您所见,Task.Run
和的两个参数不同Task.Factory.StartNew
:
TaskCreationOptions
- Task.Run
使用TaskCreationOptions.DenyChildAttach
表示子任务不能附加到父任务,请考虑以下事项:
var parentTask = Task.Run(() =>
{
var childTask = new Task(() =>
{
Thread.Sleep(10000);
Console.WriteLine("Child task finished.");
}, TaskCreationOptions.AttachedToParent);
childTask.Start();
Console.WriteLine("Parent task finished.");
});
parentTask.Wait();
Console.WriteLine("Main thread finished.");
Run Code Online (Sandbox Code Playgroud)
当我们调用时parentTask.Wait()
,childTask
即使我们指定TaskCreationOptions.AttachedToParent
了它,也不会等待,这是因为TaskCreationOptions.DenyChildAttach
禁止孩子附加到它。如果您使用Task.Factory.StartNew
代替来运行相同的代码Task.Run
,parentTask.Wait()
则会等待,childTask
因为Task.Factory.StartNew
使用TaskCreationOptions.None
TaskScheduler
- Task.Run
使用TaskScheduler.Default
表示默认任务计划程序(在线程池上运行任务的计划程序)将始终用于运行任务。Task.Factory.StartNew
另一方面,使用TaskScheduler.Current
表示当前线程的调度程序,它可能是TaskScheduler.Default
但并非总是如此。实际上,在开发Winforms
或WPF
应用程序时,需要从当前线程更新UI,为此,人们可以使用TaskScheduler.FromCurrentSynchronizationContext()
任务计划程序,如果您在使用该TaskScheduler.FromCurrentSynchronizationContext()
计划程序的任务内无意间创建了另一个长期运行的任务,则该UI将被冻结。可以在这里找到更详细的解释
因此,通常,如果您不使用嵌套的子任务,并且始终希望在线程池上执行任务,则最好使用Task.Run
,除非您有一些更复杂的方案。
除了相似之处,即 Task.Run() 是 Task.Factory.StartNew() 的简写外,在同步和异步委托的情况下,它们的行为之间存在细微差别。
假设有以下两种方法:
public async Task<int> GetIntAsync()
{
return Task.FromResult(1);
}
public int GetInt()
{
return 1;
}
Run Code Online (Sandbox Code Playgroud)
现在考虑以下代码。
var sync1 = Task.Run(() => GetInt());
var sync2 = Task.Factory.StartNew(() => GetInt());
Run Code Online (Sandbox Code Playgroud)
这里的 sync1 和 sync2 都是类型 Task<int>
但是,在异步方法的情况下会有所不同。
var async1 = Task.Run(() => GetIntAsync());
var async2 = Task.Factory.StartNew(() => GetIntAsync());
Run Code Online (Sandbox Code Playgroud)
在这种情况下, async1 的类型为Task<int>
,而 async2 的类型为Task<Task<int>>