首页 快讯正文

泰安网站制作:多线程之旅(Task 义务)

admin 快讯 2020-04-12 11 0

一、Task(义务)和ThreadPool(线程池)差别

      源码

  1、线程(Thread)是建立并发工具的底层类,然则在前几篇文章中我们先容了Thread的特点,和实例。可以很明显发现局限性(返回值欠好获取(必须在一个作用域中)),当我们线程执行完之后不能很好的举行下一次义务的执行,需要多次销毁和建立,以是不是很容易使用在多并发的情况下。

  2、线程池(ThreadPool) QueueUserWorkItem是很容易提议并发义务,也解决了上面我们的需要多次建立、销毁的性能消耗解决了,然则我们就是太简朴的,我不知道线程什么时刻竣事,也没有获取返回值的途径,也是对照尴尬的事情。

  3、义务(Task)示意一个通过或不通过线程实现的并发操作,义务是可组合的,使用延续(continuation)可将它们串联在一起,它们可以使用线程池削减启动延迟,可使用回调方式制止多个线程同时守候I/O麋集操作。

二、初识Task(义务)

  1、Task(义务)是在.NET 4.0引入的、Task是在我们线程池ThreadPool上面举行进一步的优化,以是Task默认照样线程池线程,而且是后台线程,当我们的主线程竣事时其他线程也会竣事

  2、Task建立义务,也和之前差不多

 /// <summary>
        /// Task 的使用
        /// Task 的建立照样差不多的
        /// </summary>
        public static void Show()
        {
            //实例方式
            Task task = new Task(() =>
            {
                Console.WriteLine("无返回参数的委托");
            });

            //无参有返回值
            Task<string> task1 = new Task<string>(() =>
            {
                return "我是返回值";
            });

            //有参有返回值
            Task<string> task2 = new Task<string>(x =>
            {
                return "返回值 -- " + x.ToString();
            }, "我是输入参数");
            //开启线程
            task2.Start();
            //获取返回值 Result会堵塞线程获取返回值
            Console.WriteLine(task2.Result);

            //使用线程工厂建立 无参数无返回值线程
            Task.Factory.StartNew(() =>
            {
                Console.WriteLine("这个是线程工厂建立");
            }).Start();

            //使用线程工厂建立 有参数有返回值线程
            Task.Factory.StartNew(x =>
            {
                return "返回值 -- " + x.ToString(); ;
            }, "我是参数");

            //直接静态方式运行
            Task.Run(() =>
            {
                Console.WriteLine("无返回参数的委托");
            });
        }
View Code

说明

  1、事实上Task.Factory类型自己就是TaskFactory(义务工厂),而Task.Run(在.NET4.5引入,4.0版本挪用的是后者)是Task.Factory.StartNew的简写法,是后者的重载版本,更天真简朴些。

  2、挪用静态Run方式会自动建立Task工具并立刻挪用Start

  3、Task.Run等方式启动义务并没有挪用Start,由于它建立的是“热”义务,相反“冷”义务的建立是通过Task组织函数。

三、Task(义务进阶)

  1、Wait 守候Task线程完成才会执行后续动作

泰安网站制作:多线程之旅(Task 义务) 第1张
 //建立一个线程使用Wait堵塞线程
            Task.Run(() =>
            {
                Console.WriteLine("Wait 守候Task线程完成才会执行后续动作");
            }).Wait();
View Code

  2、WaitAll 守候Task[] 线程数组所有执行乐成之后才会执行后续动作

泰安网站制作:多线程之旅(Task 义务) 第1张
            //建立一个装载线程的容器
            List<Task> list = new List<Task>();
            for (int i = 0; i < 10; i++)
            {
                list.Add(Task.Run(() =>
                {
                    Console.WriteLine("WaitAll 执行");
                }));
            }
            Task.WaitAll(list.ToArray());
            Console.WriteLine("Wait执行完毕");
View Code

  3、WaitAny 守候Task[] 线程数组任一执行乐成之后就会执行后续动作

泰安网站制作:多线程之旅(Task 义务) 第1张
//建立一个装载线程的容器
            List<Task> list = new List<Task>();
            for (int i = 0; i < 10; i++)
            {
                list.Add(Task.Run(() =>
                {
                    Console.WriteLine("WaitAny 执行");
                }));
            }
            Task.WaitAny(list.ToArray());
            Console.WriteLine("WaitAny 执行完毕");
View Code

  4、WhenAll 守候Task[] 线程数组所有执行乐成之后才会执行后续动作、与WaitAll差别的是他有回调函数ContinueWith

泰安网站制作:多线程之旅(Task 义务) 第1张
 //建立一个装载线程的容器
            List<Task> list = new List<Task>();
            for (int i = 0; i < 10; i++)
            {
                list.Add(Task.Run(() =>
                {
                    Console.WriteLine("WhenAll 执行");
                }));
            }
            Task.WhenAll(list.ToArray()).ContinueWith(x =>
            {
                return x.AsyncState;
            });
            Console.WriteLine("WhenAll 执行完毕");
View Code

  5、WhenAny 守候Task[] 线程数组任一执行乐成之后就会执行后续动作、与WaitAny差别的是他有回调函数ContinueWith

泰安网站制作:多线程之旅(Task 义务) 第1张
//建立一个装载线程的容器
            List<Task> list = new List<Task>();
            for (int i = 0; i < 10; i++)
            {
                list.Add(Task.Run(() =>
                {
                    Console.WriteLine("WhenAny 执行");
                }));
            }
            Task.WhenAny(list.ToArray()).ContinueWith(x =>
            {
                return x.AsyncState;
            });
            Console.WriteLine("WhenAny 执行完毕");
            Console.ReadLine();
View Code

四、Parallel 并发控制

  1、是在Task的基础上做了封装 4.5,使用起来对照简朴,若是我们执行100个义务,只能用到10个线程我们就可以使用Parallel并发控制

泰安网站制作:多线程之旅(Task 义务) 第1张
        public static void Show5()
        {
            //第一种方式是
            Parallel.Invoke(() =>
            {
                Console.WriteLine("我是线程一号");
            }, () =>
            {
                Console.WriteLine("我是线程二号");
            }, () =>
            {
                Console.WriteLine("我是线程三号");
            });

            //for 方式建立多线程
            Parallel.For(0, 5, x =>
            {
                Console.WriteLine("这个看名字就知道是for了哈哈 i=" + x);
            });

            //ForEach 方式建立多线程
            Parallel.ForEach(new string[] { "0", "1", "2", "3", "4" }, x => Console.WriteLine("这个看名字就知道是ForEach了哈哈 i=" + x));

            //这个我们包一层,就不会卡主界面了
            Task.Run(() =>
            {
                //建立线程选项
                ParallelOptions parallelOptions = new ParallelOptions()
                {
                    MaxDegreeOfParallelism = 3
                };
                //建立一个并发线程
                Parallel.For(0, 5, parallelOptions, x =>
                {
                    Console.WriteLine("限制执行的次数");
                });
            }).Wait();
            Console.WriteLine("**************************************");

            //Break  Stop  都不推荐用
            ParallelOptions parallelOptions = new ParallelOptions();
            parallelOptions.MaxDegreeOfParallelism = 3;
            Parallel.For(0, 40, parallelOptions, (i, state) =>
            {
                if (i == 20)
                {
                    Console.WriteLine("线程Break,Parallel竣事");
                    state.Break();//竣事Parallel
                                  //return;//必须带上
                }
                if (i == 2)
                {
                    Console.WriteLine("线程Stop,当前义务竣事");
                    state.Stop();//当前这次竣事
                                 //return;//必须带上
                }
                Console.WriteLine("我是线程i=" + i);
            });
        }
View Code

 五、多线程实例

  1、代码异常我信息人人都不生疏,好比我刚刚写代码经常会报 =>工具未定义null  的真的是让我心痛了一地,那我们的多线程中怎么去处置代码异常呢? 和我们经常写的同步方式不一样,同步方式遇到错误会直接抛出,当是若是我们的多线程中泛起代码异常,那么这个异常会自动通报挪用Wait 或者 Task<TResult> 的Result属性上面。义务的异常会将自动捕捉而且抛给挪用者,为了确保讲述所有的异常,CLR会将异常封装到AggregateExcepiton容器中,这容器是公然了InnerExceptions属性中包罗所有捕捉的异常,然则若是我们的线程没有守候竣事不会获取到异常。

泰安网站制作:多线程之旅(Task 义务) 第1张
class Program
      {
         static void Main(string[] args)
         {
              try
             {
                  Task.Run(() =>
                  {
                      throw new Exception("错误");
                 }).Wait();
             }
             catch (AggregateException axe)
             {
                 foreach (var item in axe.InnerExceptions)
                 {
                     Console.WriteLine(item.Message);
                 }
            }
             Console.ReadKey();
         }
     }
View Code 泰安网站制作:多线程之旅(Task 义务) 第1张
 /// <summary>
        /// 多线程捕捉异常 
        /// 多线程会将我们的异常吞了,由于我们的线程执行会直接执行完代码,不会去守候你捕捉到我的异常。
        /// 我们的线程中最好是不要泛起异常,自己处置好。
        /// </summary>
        public static void Show()
        {
            //建立一个多线程工厂
            TaskFactory taskFactory = new TaskFactory();
            //建立一个多线程容器
            List<Task> tasks = new List<Task>();
            //建立委托
            Action action = () =>
            {
                try
                {
                    string str = "sad";
                    int num = int.Parse(str);
                }
                catch (AggregateException ax)
                {
                    Console.WriteLine("我是AggregateException 我抓到了异常啦 ax:" + ax);
                }
                catch (Exception)
                {
                    Console.WriteLine("我是线程我已经报错了");
                }
            };
            //这个是我们经常需要做的捕捉异常
            try
            {
                //建立10个多线程
                for (int i = 0; i < 10; i++)
                {
                    tasks.Add(taskFactory.StartNew(action));
                }
                Task.WaitAll(tasks.ToArray());
            }
            catch (Exception ex)
            {
                Console.WriteLine("异常啦");
            }
            Console.WriteLine("我已经执行完了");
        }
View Code

  2、多线程作废机制,我们的Task在外部无法举行暂停 Thread().Abort() 无法很好控制,上上篇中Thread我们也讲到了Thread().Abort() 的不足之处。有问题就有解决方案。若是我们使用一个全局的变量控制,就需要不停的监控我们的变量作废线程。那么说当然有对应的方式啦。CancellationTokenSource (作废符号源)我们可以建立一个作废符号源,我们在建立线程的时刻传入我们作废符号源Token。Cancel()方式 作废线程,IsCancellationRequested 返回一个bool值,判断是不是作废了线程了。

泰安网站制作:多线程之旅(Task 义务) 第1张
 /// <summary>
        /// 多线程作废机制 我们的Task在外部无法举行暂停 Thread().Abort() 无法很好控制,我们的线程。
        /// 若是我们使用一个全局的变量控制,就需要不停的监控我们的变量作废线程。
        /// 我们可以建立一个作废符号源,我们在建立线程的时刻传入我们作废符号源Token
        /// Cancel() 作废线程,IsCancellationRequested 返回一个bool值,判断是不是作废了线程了
        /// </summary>
        public static void Show1()
        {
            //建立一个作废符号源
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
            //建立一个多线程工厂
            TaskFactory taskFactory = new TaskFactory();
            //建立一个多线程容器
            List<Task> tasks = new List<Task>();
            //建立委托
            Action<object> action = x =>
            {
                try
                {
                    //每个线程我守候2秒钟,否则
                    Thread.Sleep(2000);
                    //判断是不是作废线程了
                    if (cancellationTokenSource.IsCancellationRequested)
                    {
                        Console.WriteLine("放弃执行后面线程");
                        return;
                    }
                    if (Convert.ToUInt32(x) == 20)
                    {
                        throw new Exception(string.Format("{0} 执行失败", x));
                    }
                    Console.WriteLine("我是正常的我在执行");
                }
                catch (AggregateException ax)
                {
                    Console.WriteLine("我是AggregateException 我抓到了异常啦 ax:" + ax);
                }
                catch (Exception ex)
                {
                    //异常泛起作废后面执行的所有线程
                    cancellationTokenSource.Cancel();
                    Console.WriteLine("我是线程我已经报错了");
                }
            };
            //这个是我们经常需要做的捕捉异常
            try
            {
                //建立10个多线程
                for (int i = 0; i < 50; i++)
                {
                    int k = i;
                    tasks.Add(taskFactory.StartNew(action, k, cancellationTokenSource.Token));
                }
                Task.WaitAll(tasks.ToArray());
            }
            catch (Exception ex)
            {
                Console.WriteLine("异常啦");
            }
            Console.WriteLine("我已经执行完了");
        }
View Code

泰安网站制作:多线程之旅(Task 义务) 第19张

  3、多线程建立暂且变量,当我们启动线程之后他们执行没有先后快慢之分,正常的循环中的变量也没有作用。这个时刻就要建立一个暂且变量存储信息,解决不接见一个数据源。

泰安网站制作:多线程之旅(Task 义务) 第1张
 /// <summary>
        /// 线程暂且变量
        /// </summary>
        public static void Show2()
        {
            //建立一个线程工厂
            TaskFactory taskFactory = new TaskFactory();
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
            //建立一个委托
            Action<object> action = x =>
            {
                Console.WriteLine("传入参数 x:" + x);
            };
            for (int i = 0; i < 20; i++)
            {
                //这最主要的就是会建立20个k的暂且变量
                int k = i;
                taskFactory.StartNew(action, k);
            }
            Console.ReadLine();
        }
View Code

 泰安网站制作:多线程之旅(Task 义务) 第22张

   4、多线程锁,之前我们有提到过我们的多线程可以同时公共资源,若是我们有个变量需要加一,然则和这个时刻我们有10个线程同时操作这个会怎么样呢?

        public static List<int> list = new List<int>();
        public static int count = 0;

        public static void Show3()
        {
            //建立线程容器
            List<Task> tasks = new List<Task>();
            for (int i = 0; i < 10000; i++)
            {
                //添加线程
                tasks.Add(Task.Run(() =>
                {
                        list.Add(i);
                        count++;
                }));
            }
            Task.WaitAll(tasks.ToArray());
            Console.WriteLine("list 行数:" + list.Count + " count 总数:" + count);
            Console.ReadLine();
        }

 我们上面的代码本来是count++到10000,然则我们看到效果的时刻,我们是不是傻了呀,怎么是不是说好的10000呢,实在的数据让狗吃了?真的是小朋友有许多问号??????

 泰安网站制作:多线程之旅(Task 义务) 第23张

  5、那么我们要怎么去解决这个问题呢?方式照样有的今天我们要将到一个语法糖lock、它能做什么呢?它相当于一个代码块锁,它主要锁的是一个工具,当它锁住工具的时刻会当其他线程发生堵塞,由于当它锁住代码时刻也是锁住了工具的接见链,是其他的线程不能接见。必须守候工具接见链被释放之后才气被一个线程接见。我们的使用lock锁代码块的时刻,只管削减锁入代码块局限,由于我们锁代码之后会导致只有一个线程可以拿到数据,只管只要必须使用lock的地方使用。

  6、Lock使用要注意的地方

      1、lock只能锁引用类型的工具.

    2、不能锁空工具null某一工具可以指向Null,但Null是不需要被释放的。(请参考:熟悉周全的null)。

    3、lock 只管不要去锁string 类型虽然它是引用类型,然则string是享元模式,字符串类型被CLR“暂留”
这意味着整个程序中任何给定字符串都只有一个实例,就是这统一个工具示意了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序历程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。因此,最好锁定不会被暂留的私有或受珍爱成员。

    4、lock就制止锁定public 类型或不受程序控制的工具。例如,若是该实例可以被公然接见,则 lock(this) 可能会有问题,由于不受控制的代码也可能会锁定该工具。这可能导致死锁,即两个或更多个线程守候释放统一工具。出于同样的缘故原由,锁定公共数据类型(相比于工具)也可能导致问题。

 /// <summary>
        /// 建立一个静态工具,主要是用于锁代码块,若是是静态的就会全局锁,若是要锁实例类,就不使用静态就好了
        /// </summary>
        private readonly static object obj = new object();
        public static List<int> list = new List<int>();
        public static int count = 0;
        /// <summary>
        /// lock 多线程锁
        /// 当我们的线程接见统一个全局变量、同时接见统一个局部变量、统一个文件夹,就会泛起线程不安全
        /// 我们的使用lock锁代码块的时刻,只管削减锁入代码块局限,由于我们锁代码之后会导致只有一个线程可以
        /// 接见到我们代码块了
        /// </summary>
        public static void Show3()
        {
            //建立线程容器
            List<Task> tasks = new List<Task>();
            //锁代码
            for (int i = 0; i < 10000; i++)
            {
                //添加线程
                tasks.Add(Task.Run(() =>
                {
                    //锁代码
                    lock (obj)
                    {
                        //这个内里就只会泛起一个线程接见,资源。
                        list.Add(i);
                        count++;
                    }
                    //lock 是一个语法糖,就是下面的代码
                    Monitor.Enter(obj);

                    Monitor.Exit(obj);
                }));
            }
            Task.WaitAll(tasks.ToArray());
            Console.WriteLine("list 行数:" + list.Count + " count 总数:" + count);
            Console.ReadLine();
        }

7、总结实例篇,双色球实例。

  1、双色球:投注号码由6个红色球号码和1个蓝色球号码组成。红色球号码从01--33中选择(不重复)蓝色球号码从01--16中选择(可以跟红球重复),代码我已经实现了人人可以下载源码。只有自己多多倒腾才气让自己的手艺发展。 下一次我们async和await这两个关键字下篇纪录

泰安网站制作:多线程之旅(Task 义务) 第24张

,

Sunbet 申博

Sunbet, 申博致力与代理真诚合作的官方网站www.jrd18.com!Sunbet,致力于用户诚信服务的官方网站!

版权声明

本文仅代表作者观点,
不代表本站诚信在线的立场。
本文系作者授权发表,未经许可,不得转载。