Task多线程的常规用法

Task被认为优于线程池

利用拉姆达表达式和本身的泛型支持,可以轻松实现指向常用的方法。

注意:委托(含拉姆达表达式)中调用的方法,其参数如果是变量,它的值取决于运行的那一刻内存里的值。如果希望在创建任务的时候值是固定的,必须用object参数state作为创建任务的状态,把值给进去。

以下两种用法,结果是不同的。

static void Main(string[] args)
        {
            Task t;
            for (int i = 0; i < 10; i++)
            {
                //t = new Task(print1,i+1);
                t = new Task(() => print1(i + 1));
                t.Start();
            }
            Console.ReadKey();
        }
        static void print1(object x)
        {
            Console.Write($"{x}	");
        }

Task常规用法为:

1、有参action或func:(//下面两行中的1500和2500都是object类型,t1指向Func,t2指向Action

var t1 = new Task<int>(add2, 1500);
var t2 = new Task(add1, 2500);
            t1.Start();
            t2.Start();

2、对无参action或func,除了上面的用法,也可以简写为:

var t1 = Task.Run(say);
            var t2 = Task.Run<int>(say1);

3、某线程结束后可以直接回调

var t = new Task(DoSth);
            t.Start();
            t.ContinueWith(showit, "hi");

如上,后两句可以调换位置。

4、取结果用Task对象的Result属性。会堵塞主线程。

static void Main(string[] args)
        {
            DateTime d1 = DateTime.Now;
            var t1 = Task.Run(say);
            var t2 = Task.Run<int>(say1);
            Console.WriteLine(t2.Result);
            Task.WhenAll(t1,t2).ContinueWith(showtime, d1);
            Console.WriteLine("hi in main");
            Console.ReadKey();
        }
        static void say()
        {
            Console.WriteLine("hi in say");
        }
        static int say1()
        {
            Thread.Sleep(1000);
            return 1;
        }
static void showtime(Task t,Object o)
        {
            DateTime dt2 = (DateTime)o,dt3=DateTime.Now;
            var span= dt3- dt2;
            Console.WriteLine(span.TotalMilliseconds);
        }

运行结果:

 另综合例1:

static void Main(string[] args)
        {
            //Task t3 = new Task(() => add1(-25,-29));
            //t3.Start();
            Task t3 = Task.Run(() => add1(-25, -29));//上两行的立即模式

            //Task<int> t4 = new Task<int>(() => add2(66,77));
            //t4.Start();
            Task<int> t4 = Task.Factory.StartNew(() => add2(66, 77));//上两行的立即模式,不用start
            //Task<int> t4 = Task.Run<int>(() => add2(66, 77));//同上一行

            t3.Wait(1000);//程序等待t3一秒(暂停)
            //Task[] ta = new Task[] { t3, t4 };
            //Task.WaitAll(ta);//同步等待(暂停)无返回
            //Task.WhenAll(ta).ContinueWith((t) => Console.WriteLine("lalala"));//异步等待(后续)when会新建一个任务

            //Task.WhenAll<int>()....
            //用法理解:异步,所有任务返回结果,结果组成结果集,被整个方法返回的任务以"result[]"属性的方式呈现。见后面的综合例2

            //以下两种,用法与all类似
            //Task.WaitAny();//返回整数,表示最先完成的task在数组中的下标。超时为-1
            //Task.WhenAny();//返回最先完成的任务

            if(t3.IsCompleted)
            {
                Console.WriteLine("ok");
            }
            else
            {
                Console.WriteLine("no");
            }
            Console.WriteLine(t4.Result);
            Console.ReadKey();
        }
        static void add1(int x, int y)
        {
            Thread.Sleep(2000);
            Console.WriteLine( x + y);
        }
        static int add2(int x,int y)
        {
            return x+y;
        }

另综合例2,多任务计算1+2+3+...+100:

 1 class Program
 2     {
 3         //无论有参无参,action都比func简单。
 4         //这里故意把问题复杂化,以做练习~_~
 5         //其实使用action,全局变量累加更容易懂。
 6         //计算1+2+...+100
 7         static void Main(string[] args)
 8         {
 9             List<Task<int>> tasks = new List<Task<int>>();
10             Task<int> t;
11             for (int i = 1; i < 100; i += 10)
12             {
13                 t = new Task<int>(add10, new MyData(i));
14                 tasks.Add(t);
15                 t.Start();
16             }
17             Task.WhenAll(tasks.ToArray()).ContinueWith(printsum);
18             Console.WriteLine($"主线程结束");
19             Console.ReadKey();
20         }
21         //计算10个数的和
22         static int add10(object x)
23         {
24             int y = ((MyData)x).v,s=0;
25             for (int i = 0; i <= 9; i++)
26             {
27                 s += y+i;
28             }
29             return s;
30         }
31         //Task<int[]>是main当中Task.WhenAll(tasks.ToArray())的返回类型
32         //含义:每个任务都返回一个整数,所有任务完成后,就返回一个新任务,它有一个整数数组作为每个任务的结果。
33         //这里也就自然是ContinueWith的参数。(兰姆达表达式的习惯)
34         //打印所有任务的结果之和(即新任务的结果属性数组里的所有元素之和)
35         static void printsum(Task<int[]> ts)
36         {
37             int s = 0;
38             foreach (var item in ts.Result)
39             {
40                 s += item;
41             }
42             Console.WriteLine($"Sum is {s}");
43         }
44     }
45     class MyData
46     {
47         public int v;
48         public MyData(int v1)
49         {
50             v = v1;
51         }
52     }

结果:

 关于任务的取消,微软提供了takon(票据、凭据)的方式。

个人觉得1、正常运行的时候,不太有这个需求;2、可以主任务和分任务都能访问到的变量(比如全局变量)来取代它,少一点学习成本。

此处略。

原文地址:https://www.cnblogs.com/wanjinliu/p/12673917.html