【问题标题】:Task.Factory.StartNew() or Task.Run() correct usage?Task.Factory.StartNew() 或 Task.Run() 正确用法?
【发布时间】:2016-04-07 17:44:23
【问题描述】:

我是多线程的新手,我很难理解处理任务的所有方法。我正在尝试在一个大程序中实现它,但没有人想无缘无故地查看所有数千行处理代码,所以我编写了一个简单的测试程序,它使用 Sleep() 而不是做实际工作并让它变得非常简单.

问题是底部。

using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            BlockingCollection<int> col = new BlockingCollection<int>();
            for(int z = 0; z<=50; z++)
            {
                col.Add(z);
            }

            Console.WriteLine("Hi from main! ThreadID: {0}", Thread.CurrentThread.ManagedThreadId);
            var parent = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Hi from PARENT! ThreadID: {0}", Thread.CurrentThread.ManagedThreadId);
                foreach (int num in col.GetConsumingEnumerable())
                {
                        Thread.Sleep(100);
                        Task.Factory.StartNew(() =>
                        {
                            Thread.Sleep(num*100);
                            if (num == 50)
                            {
                                col.CompleteAdding(); //kick out of the foreach loop
                                Thread.Sleep(25000);
                            }
                            Console.WriteLine("Hi from a Child! ThreadID: {0}", Thread.CurrentThread.ManagedThreadId);

                        }, TaskCreationOptions.AttachedToParent);
                }
                Console.WriteLine("Parent done making children. ThreadID: {0}", Thread.CurrentThread.ManagedThreadId);
            }, TaskCreationOptions.LongRunning);

            var final = parent.ContinueWith((antecedent) =>
            {
                Console.WriteLine("DONE!");
            },TaskContinuationOptions.OnlyOnRanToCompletion
                );

            Console.WriteLine("Calling Wait on final. ThreadID: {0}", Thread.CurrentThread.ManagedThreadId);
            final.Wait();
            Console.ReadLine();
        }
    }
}

代码将 51 个元素添加到阻塞集合中,然后启动一个名为 Parent 的任务。 Parent 使用阻塞集合中的项目,直到它被标记为 CompleteAdding() 被调用。

父任务长时间运行,因为我不希望它从线程池中提取线程。然后它应该生成附加的子任务,这些任务会休眠一段时间,如果元素是 '50',它会调用 CompleteAdding()

然后有一个名为final 的延续,它仅在父级运行完成时运行。并且只有当阻塞集合也完成添加并且所有子任务也完成时,父级才能完成。

然后我从 main 调用 final.Wait()(在我的实际程序中,final.Wait() 可能在 OnFormClosing() 方法中。)

做这一切的重点是能够确保在程序关闭之前所有当前正在运行的工作都完成了,并且所有排队的工作也完成了。

这就是问题所在。我在很多地方都看到我应该使用Task.Run() 而不是Task.Factory.StartNew(),所以我尝试将它们换掉,然后我开始收到错误消息说Error CS1643 Not all code paths return a value in lambda expression of type 'Func&lt;Task&gt;' 如果我不想要返回值,为什么我需要返回值。这也是正确使用Task.Factory.StartNew() 还是我在做一些有风险/危险的事情?

note 不确定它是否重要,但在程序中,我将在睡眠中使用此设置,例如匹配数据并将其发送到 oracle 数据库。我们也不能高于 .NET 4.6

【问题讨论】:

  • 你不需要 BlockingCollection 来启动 50 个任务,只需使用普通的 for 循环。哦,还有捕获循环变量。到任务运行时,num 很可能是您所有任务的 50。
  • 在我看来,您试图绕过 TAP 系统的设计目的,而是尝试使用“长时间运行的线程”自己重新创建它,因为您不希望从线程池中拉出一个线程。为什么?这正是 TAP 系统所做的,它安排一大堆要完成的任务,然后确保它们全部完成。
  • 我知道这一点。目标不是启动 50 个任务,而是确保从队列中拉出所有“工作”,在这种情况下是一个 int,直到我们确定不会再添加,并且所有工作在程序之前完成退出。但是,如果这一切最终都被应用,它是一个工作项对象,它具有指令和数据,指令应该对其进行操作,并且工作项是在新数据可用时创建的。可能是一小时内 50 个项目,也可能是 20 分钟内 50000 个项目。没有办法知道。
  • @CodingGorilla 我们保证他们完成了。如果我启动一个等待 10 分钟的任务并创建一个文本文件,并且在任务启动后立即关闭程序是创建文本文件吗?重点是在所有工作完成之前程序不能退出。
  • @user5999614 不,如果您结束该过程,那么显然任务将无法完成,但您在此处拥有的代码就是如此。如果拥有进程结束,您将无法运行代码。如果您的问题是在工作完成之前阻止main 退出,则有 其他方法可以处理该问题,重新创建 TAP 和线程队列 (IMO) 不是解决方法用它。

标签: c# multithreading task-parallel-library


【解决方案1】:

让我们简化Task.Run 版本的代码:

Task.Run(() => { }, TaskCreationOptions.LongRunning);

此代码错误:there is no overload of Task.Run() accepting TaskCreationOptions。但是 C# 编译器报告的错误消息(“并非所有代码路径都在 'Func&lt;Task&gt;' 类型的 lambda 表达式中返回值”)也是错误的。我认为这是 C# 编译器中的一个错误,所以I reported it

【讨论】:

    【解决方案2】:

    Microsoft 的文档将Task.Run 描述为异步运行任务的“更简单的方式”。它和Task.Factory.StartNew 都返回Task 并且语法相同。我有兴趣查看导致编译错误的代码。但是,如果您的代码与 Task.Factory.StartNew 一起使用,我看不出有什么令人信服的理由来更改它。

            Task.Run(() =>
            {
                Console.Write("Doing");
                Console.Write("Something");
            });
    
            Task.Factory.StartNew(() =>
            {
                Console.Write("Doing");
                Console.Write("Something");
            });
    

    【讨论】:

    • 您可以按原样复制粘贴我的代码,只需将 Task.Factory.StartNew 更改为 Task.Run 即可重现错误。
    猜你喜欢
    • 2015-06-23
    • 2016-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-30
    • 1970-01-01
    • 2023-03-22
    • 2013-01-21
    相关资源
    最近更新 更多