Task基础-创建Task,Task传参,获取Task返回值

  1. Task基础介绍
  2. Task的创建
  3. 获取Task的执行结果
  4.  补充细节

1、Task基础介绍

Task类是Task Programming Library(TPL)中最核心的一个类,下面我将会像大家展示如何使用一些方法来创建不同类型的Task,取消Task,等待Task执行完成,获取Task执行后的结果和对异常进行处理。

我们用静态方法:Task.Factory.StartNew()来创建了一个最简单的Task--在屏幕上打印一句话。这段代码确实简单,而且都没有任何输入和需要返回的结果。

  Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Hello World");
            });

 

2、Task的创建

为了执行一个简单的Task,一般进行以下步骤:

第一步,创建一个Task类的实例

第二步,传入一个Action委托,这个委托中的方法就是这个Task运行时要执行的方法,而这个委托必须作为Task构造函数的一个参数传入。传入委托作为参数的时候有多种方法:传入匿名委托,lambda表达式等。

第三步,调用Task实力的start()方法来运行。

当这个Task实例开始运行的时候,它就被传给了内部的一个task scheduler,这个scheduler负责把我们创建的task交给底下的线程去执行。
    下面就看看代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Task创建
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //第一种,使用委托和方法
            Task task1 = new Task(new Action(printMsg));
            

            //第二种,使用匿名委托
            Task task2 = new Task(delegate { 
                printMsg ();
            });

            //第三种,使用lambda和方法
            Task task3 = new Task(()=>printMsg ());

            //第四种,使用lambda和匿名方法
            Task task4 = new Task(()=> {
                printMsg();
            });

            task1.Start();
            task2.Start();
            task3.Start();
            task4.Start();
            //第四种,使用Task.Run(),Task.Run()的返回值也是一个Task
            Task.Run(()=> {
                printMsg();
            });

 

Console.Read(); }
static void printMsg() { Console.WriteLine("hello world"); } } }

上面代码创建Task的方法和我们之前的第一段代码的创建Task的方法不同。在之前我们采用的是Task.Factory.StartNew()方法来创建的,这个方法创建Task并且开始运行Task,其实两端代码的结果是一样的,这里给出一点建议:如果这是想简单的创建一个Task,那么使用Factory.NewStart()来创建,很简便,如果像对所创建的Task附加更多的定制和设置特定的属性,那么还是得一步一步的按照我们说的那些步骤来。

3、获取Task的执行结果

在创建Task的时候,我们在构造函数中传入了一个System.Action的委托,如果我们想要把一些参数传入到Task中,那么我们可以Task的另一个构造函数public Task(Action<object> action, object state),传入System.Action<object>的委托,其中的那个object就是我们传入的参数。举例如下:

using System;
using System.Threading.Tasks;

namespace Task传入参数
{
    internal class Program
    {
        static void Main(string[] args)
        {
            string[] messages = { "First task", "Second task","Third task", "Fourth task" };
            foreach (var item in messages)
            {
                //第一种方法
                Task task = new Task((s) => printMsg((string)s), item);
                //第二种方法
                //Task task = new Task(()=>printMsg(item));
                task.Start();
                
            }
            Console.WriteLine("Main method complete. Press enter to finish.");
            Console.ReadLine();
        }

        static void printMsg(string msg)
        {
            Console.WriteLine($"print message:{msg}");
        }
    }
}

注意:我们在传入参数后,必须把参数转换为它们原来的类型,然后再去调用相应的方法。例子中,因为System.Action对应的方法是printMessage()方法,而这个方法的要求的参数类型是string,所以要转换为string。

两种传入参数的方法有什么区别?目前我还不太清楚,肯是因为第二种方法会导致闭包。

  想向Task传入参素,只能用System.Action<object>

3、获取Task的执行结果

如果要获取Task的结果,那么在创建Task的时候,就要采用Task<T>来实例化一个Task,其中的那个T就是task执行完成之后返回结果的类型。之后采用Task实例的Result属性就可以获取结果,只有在task执行完成之后,才能获取到Result的值。
    代码显示如下:

1、使用Task开启一个线程,计算10以内求和,并获得返回值

using System;
using System.Threading.Tasks;
using System.Threading;

namespace Task获得返回值
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Task<int> task = new Task<int>(() => getSum());
            task.Start();

            Console.WriteLine($"task result is {task.Result}");
            Console.ReadLine();
        }

        static int getSum()
        {
            int sum = 0;
            for (int i = 0; i < 10; i++)
            {
                sum = sum + i;
                Thread.Sleep(100);
            }
            return sum;
        }
    }    
}

 

2、对上面的代码进行修改,计算任意数字内的求和,需要输入一个参数,并获得返回值。

using System;
using System.Threading.Tasks;
using System.Threading;

namespace Task获得返回值
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Task<int> task = new Task<int>((l) => getSum((int)l),10);
            task.Start();

            Console.WriteLine($"task result is {task.Result}");
            Console.ReadLine();
        }

        static int getSum(int len)
        {
            int sum = 0;
            for (int i = 0; i < len; i++)
            {
                sum = sum + i;
                Thread.Sleep(100);
            }
            return sum;
        }
    }    
}

3、通过Task.Factory.StartNew<TResualt>创建一个Task,并获得结果。

using System;
using System.Threading.Tasks;

namespace TaskFactory获得返回值
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Task<int> task = Task.Factory.StartNew<int>((n) => {

                int sum = 0;
                for (int i = 0; i < (int)n; i++)
                {
                    sum = sum + i;
                }
                return sum;
            }, 100);

            Console.WriteLine($"sum = {task.Result}");
            Console.ReadLine();
        }
    }
}

 

4、补充细节

在创建Task的时候,Task有很多的构造函数的重载,一个主要的重载就是传入TaskCreateOptions的枚举:     
    TaskCreateOptions.None:用默认的方式创建一个Task
    TaskCreateOptions.PreferFairness:请求scheduler尽量公平的执行Task(后续文章会将是,Task和线程一样,有优先级的)
    TaskCreateOptions.LongRunning:声明Task将会长时间的运行。
    TaskCreateOptions.AttachToParent:因为Task是可以嵌套的,所以这个枚举就是把一个子task附加到一个父task中。

    最后要提到的一点就是,我们可以在Task的执行体中用Task.CurrentId来返回Task的唯一表示ID(int)。如果在Task执行体外使用这个属性就会得到null。

 

posted on 2022-06-21 22:49  hanzq_go  阅读(5062)  评论(0编辑  收藏  举报

导航