C# 多线程编程及其几种方式
引言:
进程(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
部分内容来自:https://www.cnblogs.com/tianqing/p/6970331.html
C# 多线程编程及其几种方式的更多相关文章
- JAVA多线程实现的四种方式
Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService.Cal ...
- JAVA多线程实现的三种方式
JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...
- 【转】JAVA多线程实现的四种方式
原文地址:http://www.cnblogs.com/felixzh/p/6036074.html Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callabl ...
- JAVA多线程实现的四种方式(转自https://www.cnblogs.com/felixzh/p/6036074.html)
Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService.Cal ...
- AJPFX关于JAVA多线程实现的三种方式
JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...
- 关于Java多线程(JAVA多线程实现的四种方式)
Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService.Cal ...
- 如何实现有返回值的多线程 JAVA多线程实现的三种方式
可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口.执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable ...
- Java 异步编程的几种方式
前言 异步编程是让程序并发运行的一种手段.它允许多个事情同时发生,当程序调用需要长时间运行的方法时,它不会阻塞当前的执行流程,程序可以继续运行,当方法执行完成时通知给主线程根据需要获取其执行结果或者失 ...
- Thread类的常用方法_sleep和创建多线程程序的第二种方式_实现Runnable接口
sleep方法是在Thread类中的一个静态方法,当一个线程调用了sleep方法,被调用的那个线程就会暂时的让出指定时间的CPU执行权,在这段时间也不会参与CPU的调度,当时间到了之后,就会重新回到就 ...
- 创建多线程程序的第一种方式_创建Thread类的子类
创建多线程程序的第一种方式:创建Thread类的子类java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类 实现步骤: 1.创建一个Thread类的子类 ...
随机推荐
- 在.NET Core中使用异步多线程高效率的处理大量数据的最佳实践
目录 一.引言 二.假设场景 三.解决方案 四.示例代码 一.引言 处理大量数据是一个常见的需求,传统的同步处理方式往往效率低下,尤其是在数据量非常大的情况下.本篇将介绍一种高效的多线程异步处理大数据 ...
- tc端口流量控制(带宽限速)
tc qdisc add dev ens192 root handle 1: htbtc class add dev ens192 parent 1: classid 1:1 htb rate 80m ...
- IDEA跳转到上一个下一个方法的快捷键
假如一个方法很不规范,写了好几百行,你想去下一个方法,如果用鼠标往下滑,会挺崩溃的.或者有的时候,就是需要一个一个方法往下看,那么IDEA有没有这样方便的快捷键呢?是有的:按住Alt键,再按上/下方向 ...
- ESLint is disabled since its execution has not been approved or denied yet
VS Code 装好ESLint 插件报黄线的问题,具体解决方法如下所示: ESLint is disabled since its execution has not been approved o ...
- 从新手到专家:如何设计一套亿级消息量的分布式IM系统
本文原作者Chank,原题"如何设计一个亿级消息量的 IM 系统",为了提升内容质量,本次有修订和改动. 1.写有前面 本文将在亿级消息量.分布式IM系统这个技术前提下,分析和总结 ...
- Kubernetes系列(四) - Pod和Pod调度
目录 1. Pod的组成部分 2. Pod的优势 3. Pod的两种分类 3.1 普通的pod 3.2 静态pod(static pod) 4. 控制器controller的特点 4.1 Deploy ...
- 创建Windows service使用FluentScheduler定时刷新网页
我们都知道iis的程序池默认的闲置回收时间是20分钟, 如果是自己的服务器,我们可以设置成0,闲置不回收. 这样网站就不会出现每隔20分钟没有访客访问就出现打开非常慢的情况. 但是,如果个别网站不是用 ...
- 性能测试工具_nGrinder
1. ngrinder-controller-3.4.3.war 放置到tomcat的webapps目录下:2. 启动tomcat;3. 访问地址: http://localhost:8080/ngr ...
- Java接口-详解
一.基本概念 接口(Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合.接口通常以interface来声明.一个类通过继承接口的方式,从而来继承接口的抽象方法. 如果一个类只由 ...
- C:条件编译
问题 #ifdef HELIB_DEBUG long pa, pb; std::vector<long> slots; decryptBinaryNums(slots, a, *dbgKe ...