c# - tpl - Task.Run()和Task.Factory.StartNew()有什么区别




c# tpl (4)

人们已经提到

Task.Run(A);

相当于

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

但是没有人提到

Task.Factory.StartNew(A);

等效于:

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

如您所见, Task.RunTask.Factory.StartNew 两个参数不同:

  1. TaskCreationOptions 使用 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.");

    当我们调用 parentTask.Wait() ,即使我们 TaskCreationOptions.AttachedToParent 指定 TaskCreationOptions.AttachedToParent ,也不会等待 TaskCreationOptions.AttachedToParent ,这是因为 TaskCreationOptions.DenyChildAttach 禁止孩子附加到它。 如果使用 Task.Factory.StartNew 而不是 Task.Run 运行相同的代码,则 parentTask.Wait() 将等待 childTask 因为 Task.Factory.StartNew 使用 TaskCreationOptions.None

  2. TaskScheduler 使用 TaskScheduler.Default ,这意味着将始终使用默认任务计划程序(在线程池上运行任务的计划程序)来运行任务。 另一方面, Task.Factory.StartNew 使用 TaskScheduler.Current 表示当前线程的调度程序,它可能是 TaskScheduler.Default 但并非总是如此。 实际上,在开发 WinformsWPF 应用程序时,如果您在使用 TaskScheduler.FromCurrentSynchronizationContext() 调度程序的任务内无意间创建了另一个长期运行的任务,则需要从当前线程更新UI,为此人员请使用 TaskScheduler.FromCurrentSynchronizationContext() 任务调度程序。用户界面将被冻结。 可以在 here 找到更详细的解释

因此,通常,如果您不使用嵌套子任务,并且始终希望在线程池上执行任务,则最好使用 Task.Run ,除非您有一些更复杂的方案。

我有方法:

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() 之间有什么区别。 创建Task实例后,他们两个都立即使用ThreadPool并启动Method()。 什么时候应该使用第一个变量,什么时候应该使用第二个变量?


在调用两个服务的应用程序中,我比较了Task.Run和 Task.Factory.StartNew 。 我发现我俩都可以正常工作。 但是,第二个更快。


第二种方法 Task.Run 在.NET框架的更高版本(.NET 4.5中)中已引入。

但是,第一种方法 Task.Factory.StartNew 让您定义有关要创建的线程的许多有用的东西,而 Task.Run 不提供此功能。

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

为了避免这种情况,您可以做的一件事就是在单独的线程中运行任务。 一个新创建的线程 ,专用于此任务,一旦完成任务,该 线程 将被销毁。 无法 通过 Task.Run 实现此 Task.Run ,而可以通过 Task.Factory.StartNew ,如下所示:

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

如此处所述:

因此,在.NET Framework 4.5开发人员预览版中,我们引入了新的Task.Run方法。 这绝不会淘汰 Task.Factory.StartNew, 而应被认为是使用 Task.Factory.StartNew 的快速方法, 而无需指定一堆参数。 这是捷径。 实际上,Task.Run实际上是根据与Task.Factory.StartNew相同的逻辑实现的,只是传入一些默认参数。 将Action传递给Task.Run时:

Task.Run(someAction);

完全等同于:

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

请参阅描述差异的 博客文章 。 基本上在做:

Task.Run(A)

与执行相同:

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




task-parallel-library