我有方法:

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");
}

我想在一个新任务中开始这个方法。 我可以像这样开始新的任务

var task = Task.Factory.StartNew(new Action(Method));

或者这个

var task = Task.Run(new Action(Method));

但是Task.Run()和Task.Factory.StartNew()之间有什么区别吗?它们都在使用ThreadPool,并在创建Task实例后立即启动Method()。什么时候用第一种,什么时候用第二种?


当前回答

请参阅这篇描述区别的博客文章。主要做:

Task.Run(A)

就等于做了:

Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);   

其他回答

这个任务。Run在新的。net框架版本中被引入,推荐使用。

从. net Framework 4.5开始,Task。运行方法是 推荐的启动计算绑定任务的方法。使用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);
}

请参阅这篇描述区别的博客文章。主要做:

Task.Run(A)

就等于做了:

Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);   

除了Task.Run()是Task.Factory.StartNew()的简写之外,在同步委托和异步委托的情况下,它们的行为有细微的区别。

假设有以下两种方法:

public async Task<int> GetIntAsync()
{
    return Task.FromResult(1);
}

public int GetInt()
{
    return 1;
}

现在考虑下面的代码。

var sync1 = Task.Run(() => GetInt());
var sync2 = Task.Factory.StartNew(() => GetInt());

这里sync1和sync2的类型都是Task<int>

然而,不同之处在于异步方法。

var async1 = Task.Run(() => GetIntAsync());
var async2 = Task.Factory.StartNew(() => GetIntAsync());

在这个场景中,async1的类型是Task<int>,而async2的类型是Task<Task<int>>

根据Stephen Cleary的这篇文章,Task.Factory.StartNew()是危险的:

I see a lot of code on blogs and in SO questions that use Task.Factory.StartNew to spin up work on a background thread. Stephen Toub has an excellent blog article that explains why Task.Run is better than Task.Factory.StartNew, but I think a lot of people just haven’t read it (or don’t understand it). So, I’ve taken the same arguments, added some more forceful language, and we’ll see how this goes. :) StartNew does offer many more options than Task.Run, but it is quite dangerous, as we’ll see. You should prefer Task.Run over Task.Factory.StartNew in async code.

以下是真正的原因:

Does not understand async delegates. This is actually the same as point 1 in the reasons why you would want to use StartNew. The problem is that when you pass an async delegate to StartNew, it’s natural to assume that the returned task represents that delegate. However, since StartNew does not understand async delegates, what that task actually represents is just the beginning of that delegate. This is one of the first pitfalls that coders encounter when using StartNew in async code. Confusing default scheduler. OK, trick question time: in the code below, what thread does the method “A” run on?

Task.Factory.StartNew(A);

private static void A() { }

你知道这是个陷阱问题,对吧?如果你回答“一根线” pool thread ",很抱歉,这是错误的。A会继续 不管TaskScheduler当前正在执行什么!

所以这意味着如果一个操作完成,它可能会运行在UI线程上,并且由于一个延续,它会封送回UI线程,Stephen Cleary在他的帖子中解释得更详细。

在我的例子中,我试图在加载一个视图的数据网格时在后台运行任务,同时还显示一个繁忙的动画。当使用Task.Factory.StartNew()时,繁忙动画没有显示,但当我切换到Task.Run()时,动画显示正常。

详情请见https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html

第二种方法,Task。在. net框架的最新版本(. net 4.5)中引入了Run。

但是,第一个方法Task.Factory。StartNew,让你有机会定义关于你想要创建的线程的很多有用的东西,而Task。Run没有提供这个。

例如,假设您想创建一个长时间运行的任务线程。如果线程池中的一个线程将用于此任务,则可以认为这是对线程池的滥用。

为了避免这种情况,您可以在单独的线程中运行任务。一个新创建的线程,它将专门用于此任务,并且在任务完成后将被销毁。你不能通过任务来实现这一点。运行,而您可以使用Task.Factory这样做。StartNew,如下所示:

Task.Factory.StartNew(..., TaskCreationOptions.LongRunning);

如这里所述:

因此,在.NET Framework 4.5开发人员预览中,我们已经介绍了 新任务。运行方法。这不会淘汰Task.Factory.StartNew, 而应该简单地认为这是一种快速使用的方法 task。factory。startnew而不需要指定一堆 参数。这是一条近路。事实上,任务。Run实际上是 以Task.Factory.StartNew相同的逻辑实现, 只是传入一些默认参数。当您将一个Action传递给 的任务。运行:

Task.Run(someAction);

这完全等价于:

Task.Factory.StartNew(someAction, 
    CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);