引言:
进程(process):应用程序的实例要使用的资源的集合。每个进程被赋予了一个虚拟地址空间,确保在一个进程中使用的代码和数据无法由另一个进程访问。
线程(thread):程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,及不同的线程可以执行相同的函数。
多线程编程优缺点,
优点:可以提高CPU利用率。
缺点:
1、线程越多占用内存越多;
2、多线程需要协调和管理,需要CPU时间跟踪线程;
3、线程之间对共享资源会相互影响,必须解决竞用共享资源问题;
4、线程太多会导致控制太复杂,可能造成很多BUG
一、用Thread类开启线程
关键字:前台线程,后台线程,线程优先级,线程休眠,线程阻塞。
前台线程:主线程结束后,前台线程将继续执行才结束。
后台线程:主线程结束后,后台线程不管有没有完成也结束线程
1 class MultiThreadingApplication { 2 static void Main(string[] args) { 3 //Thread thread1 = new Thread(new ThreadStart(Test1)); 4 Thread thread1 = new Thread(Test1);//线程传入无参数委托实例 5 //Thread thread2 = new Thread(new ParameterizedThreadStart(Test2));//正常传递 6 Thread thread2 = new Thread(Test2);//简化传递 7 thread1.Name = "线程1"; 8 thread1.IsBackground = true;//设为后台线程,主线成结束后台线程自动结束;前台线程在主线程结束后继续执行才结束 9 thread2.Name = "线程2"; 10 thread1.Start(); 11 thread2.Start("HelloWorld"); 12 Thread thread3 = new Thread(() => 13 { 14 Console.WriteLine("线程3开始"); 15 Console.WriteLine("线程3阻塞5秒钟"); 16 Thread.CurrentThread.Join(TimeSpan.FromSeconds(5)); 17 Console.WriteLine("{0}的执行方法",Thread.CurrentThread.Name); 18 Console.WriteLine("线程3结束"); 19 }); 20 thread3.Name = "线程3"; 21 thread3.Priority = ThreadPriority.Highest;//线程优先级枚举设定,此时最高,在线程池中会优先开始 22 thread3.Start(); 23 Console.WriteLine("Main()主函数线程结束"); 24 } 25 26 static void Test1() { 27 Console.WriteLine("线程1开始"); 28 Console.WriteLine("线程1休眠2秒钟"); 29 Thread.Sleep(2000); 30 Console.WriteLine("{0}调用的无参数方法",Thread.CurrentThread.Name); 31 Console.WriteLine(Thread.CurrentThread.Name+"结束"); 32 } 33 34 static void Test2(object s) { 35 Console.WriteLine("线程2开始"); 36 Console.WriteLine("{0}调用的有参数方法,方法的参数是:{1}", Thread.CurrentThread.Name, s); 37 Console.WriteLine(Thread.CurrentThread.Name + "结束"); 38 } 39 }
Thread Join()使用,在两个线程调用之间使用,阻塞当前线程,直到另一个线程结束。将两个交替的线程合并为顺序执行的线程。
比如在主线程中调用后台子线程的方法Join(),直到后台子线程执行完毕,主线程才继续执行。
1 static void Main(string[] args) 2 { 3 Console.WriteLine("主线程开始"); 4 var t = new Thread(() => 5 { 6 Console.WriteLine("后台子线程开始"); 7 Thread.Sleep(3000); 8 Console.WriteLine("后台子线程"); 9 Console.WriteLine("后台子线程结束"); 10 }); 11 t.IsBackground = true; 12 t.Start(); 13 t.Join();//等待后台子线程执行完成,才继续执行主线程,主线程在这里被阻塞。 14 //t.Join(TimeSpan.FromSeconds(5)); 15 //t.Join(5000);//主线程阻塞等待后台子线程执行5秒钟,然后继续执行主线程,5秒后不管后台子线程是否执行完成。 16 //Thread.CurrentThread.Join();//死锁情况,A线程和B线程为同一个线程,将一直阻塞。 17 Console.WriteLine("主线程结束"); 18 }
二、通过线程池类ThreadPool开启线程
1 static void Main(string[] args) 2 { 3 Console.WriteLine("主线程开始"); 4 ThreadPool.QueueUserWorkItem(DownLoadFile); 5 //使用ThreadPool线程池开启一个线程 6 ThreadPool.QueueUserWorkItem((p) => 7 { 8 Console.WriteLine("是否线程池线程:{0}",Thread.CurrentThread.IsThreadPoolThread); 9 Console.WriteLine("开启了一个线程,线程ID:{0}",Thread.CurrentThread.ManagedThreadId); 10 }); 11 Thread.Sleep(5000); 12 Console.WriteLine("主线程结束"); 13 } 14 15 static void DownLoadFile(object state) 16 { 17 Console.WriteLine("开始下载... 线程ID:" + Thread.CurrentThread.ManagedThreadId); 18 Thread.Sleep(2000); 19 Console.WriteLine("下载完成!"); 20 }
三、Task或TaskFactory方式开启,叫法不同了,任务并行编程
Task需要启动任务执行,TaskFactory创建及开始执行
Task开启的是后台线程
1 static void Main(string[] args) 2 { 3 Console.WriteLine("主线程开始"); 4 //.net framework 4.0 TPL(Task Parallel Library, 简称TPL)任务并行库方式,类似于线程处理方式,抽象级别更高 5 //任务并行,一个或多个任务同时运行 6 //系统资源的使用效率更高,可伸缩性更好 7 //TPL提供了一组简单丰富的 API,这些 API 支持等待、取消、继续、可靠的异常处理、详细状态、自定义计划等功能。 8 //降低多线程编码和并行编程的复杂度,提升开发效率 9 //使用Task开启一个任务(其实也是一个线程) 10 Task task = new Task(new Action(() => 11 { 12 Console.WriteLine("是否线程池线程:{0}", Thread.CurrentThread.IsThreadPoolThread); 13 Console.WriteLine("开启了一个线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId); 14 })); 15 task.Start(); 16 17 Task.Factory.StartNew(() => 18 { 19 Console.WriteLine("是否线程池线程:{0}", Thread.CurrentThread.IsThreadPoolThread); 20 Console.WriteLine("开启了一个线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId); 21 }); 22 23 TaskFactory tf = new TaskFactory(); 24 tf.StartNew(() => 25 { 26 Console.WriteLine("是否线程池线程:{0}", Thread.CurrentThread.IsThreadPoolThread); 27 Console.WriteLine("开启了一个线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId); 28 });29 30 Thread.Sleep(2000); 31 Console.WriteLine("主线程结束"); 32 }
CancellationToken 传入取消令牌,外部控制任务内部结束
1 static void Main(string[] args) 2 { 3 Console.WriteLine("主线程开始"); 4 var cts = new CancellationTokenSource(); 5 var task = new Task<int>(()=> { 6 return TaskAction("task", 10, cts.Token); 7 }); 8 task.Start(); 9 Console.WriteLine(task.Status); 10 cts.Cancel(); 11 Console.WriteLine(task.Status); 12 task.Wait(); 13 Thread.Sleep(2000); 14 Console.WriteLine(task.Status); 15 Console.WriteLine("task结果:{0}",task.Result); 16 Console.WriteLine("主线程结束"); 17 } 18 19 static int TaskAction(string name, int seconds, CancellationToken token) { 20 Console.WriteLine("Task:{0} is runing on Thread:{1}",name,Thread.CurrentThread.ManagedThreadId); 21 for (int i = 0; i < seconds; i++) 22 { 23 Thread.Sleep(1000); 24 if (token.IsCancellationRequested) 25 { 26 Console.WriteLine("请求取消令牌产生作用"); 27 return -1; 28 } 29 } 30 return 42 * seconds; 31 }
创建任务集合及返回结果
1 static void Main(string[] args) 2 { 3 //Task<T>,Result等待任务调用完成得到结果,有Wait的作用 4 var tasks = new List<Task<string>>() { 5 Task.Factory.StartNew(()=> { 6 return "task1"; 7 }), 8 Task.Factory.StartNew(()=> { 9 return "task2"; 10 }), 11 Task.Factory.StartNew(()=> { 12 return "task3"; 13 }), 14 }; 15 16 foreach (var task in tasks) 17 { 18 Console.WriteLine(task.Result); 19 } 20 }
异常捕获
通过Catch捕获Task的异常是AggregateException,一个被封装的异常,需要通过InnerException访问底层异常
推荐使用GetWaiter和GetResult方法访问Task的结果,可以获取到原始异常;
通过ContinueWith处理OnlyOnFaulted事件,捕获的异常也是一个被封装的异常,需要通过InnerException访问底层异常
1 static void Main(string[] args) 2 { 3 Console.WriteLine("主线程开始"); 4 Task<int> task = null; 5 try 6 { 7 task = Task.Run(() => TaskExceptionAction("Task1", 2)); 8 var result = task.Result; 9 Console.WriteLine(result); 10 } 11 catch (Exception e) 12 { 13 Console.WriteLine("Task 1 Exception,type:" + e.GetType() + ",Message:" + e.Message); 14 } 15 Console.WriteLine("=============================="); 16 try 17 { 18 task = Task.Run(() => TaskExceptionAction("Task2", 2)); 19 var result = task.GetAwaiter().GetResult(); 20 Console.WriteLine(result); 21 } 22 catch (Exception e) 23 { 24 Console.WriteLine("Task 2 Exception,type:" + e.GetType() + ",Message:" + e.Message); 25 } 26 27 var task3 = new Task<int>(() => 28 { 29 return TaskExceptionAction("task3", 2); 30 }); 31 var continueTask = Task.WhenAll(task3); 32 continueTask.ContinueWith(t => 33 { 34 Console.WriteLine("Task 3 Exception,type:" + t.Exception.GetType() + ",Message:" + t.Exception.Message); 35 }, TaskContinuationOptions.OnlyOnFaulted); 36 task3.Start(); 37 task3.Wait(); 38 Console.WriteLine("主线程结束"); 39 } 40 41 static int TaskExceptionAction(string name, int seconds) 42 { 43 Console.WriteLine("任务:{0} 在线程:{1}上运行", name, Thread.CurrentThread.ManagedThreadId); 44 Thread.Sleep(seconds * 1000); 45 throw new Exception("Error"); 46 }
多任务的串行化
1 static void Main(string[] args) 2 { 3 Console.WriteLine("主线程开始"); 4 var queryTask = new Task<string>(() => 5 { 6 Console.WriteLine("Start queryTask!"); 7 return "QueryResult"; 8 }); 9 var analyzeTask = queryTask.ContinueWith((queryResult) => 10 { 11 Console.WriteLine("Start AnalyzeTask!"); 12 return "Analyzed Data:" + queryResult.Result; 13 }); 14 var reportTask = analyzeTask.ContinueWith((analyzeResult) => 15 { 16 Console.WriteLine("Start ReportTask!"); 17 return "Reporting Data:" + analyzeResult.Result; 18 }); 19 queryTask.Start(); 20 Console.WriteLine(reportTask.Result); 21 Console.WriteLine("主线程结束"); 22 }
除非所有子任务(子任务的子任务)结束运行,否则创建任务(父任务)不会认为已经结束
父任务异常不会影响子任务执行
子任务异常不会影响父任务执行
1 static void Main(string[] args) 2 { 3 Console.WriteLine("主线程开始"); 4 var parentTask = Task.Factory.StartNew(() => 5 { 6 Console.WriteLine("创建一个父任务"); 7 var subTask = Task.Factory.StartNew(() => 8 { 9 Console.WriteLine("创建一个子任务"); 10 throw new Exception("subTask Error"); 11 }, TaskCreationOptions.AttachedToParent); 12 Console.WriteLine("父任务结束"); 13 }); 14 Thread.Sleep(5000); 15 Console.WriteLine("主线程结束"); 16 }
四、异步委托开启多线程
//TODO