引言:

进程(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# 多线程编程及其几种方式的更多相关文章

  1. JAVA多线程实现的四种方式

    Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService.Cal ...

  2. JAVA多线程实现的三种方式

    JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...

  3. 【转】JAVA多线程实现的四种方式

    原文地址:http://www.cnblogs.com/felixzh/p/6036074.html Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callabl ...

  4. JAVA多线程实现的四种方式(转自https://www.cnblogs.com/felixzh/p/6036074.html)

    Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService.Cal ...

  5. AJPFX关于JAVA多线程实现的三种方式

    JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...

  6. 关于Java多线程(JAVA多线程实现的四种方式)

    Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService.Cal ...

  7. 如何实现有返回值的多线程 JAVA多线程实现的三种方式

    可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口.执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable ...

  8. Java 异步编程的几种方式

    前言 异步编程是让程序并发运行的一种手段.它允许多个事情同时发生,当程序调用需要长时间运行的方法时,它不会阻塞当前的执行流程,程序可以继续运行,当方法执行完成时通知给主线程根据需要获取其执行结果或者失 ...

  9. Thread类的常用方法_sleep和创建多线程程序的第二种方式_实现Runnable接口

    sleep方法是在Thread类中的一个静态方法,当一个线程调用了sleep方法,被调用的那个线程就会暂时的让出指定时间的CPU执行权,在这段时间也不会参与CPU的调度,当时间到了之后,就会重新回到就 ...

  10. 创建多线程程序的第一种方式_创建Thread类的子类

    创建多线程程序的第一种方式:创建Thread类的子类java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类 实现步骤: 1.创建一个Thread类的子类 ...

随机推荐

  1. 金Gien乐道 | 8月热点回顾

    中电金信2023年中工作会议:加快推进以"源启"为基石的业务模式转型 ​​8月12日,中电金信2023年中工作会议在上海召开.会议贯彻落实中国电子2023年中工作会议精神,总结上半 ...

  2. 【Linux】【虚拟机】 IP地址的动态与静态设置

    目录 配置文件的修改 配置文件的修改 vim /etc/sysconfig/network-scripts/ifcfg-ens33 IP配置方式(不指定:none,静态:static,动态:dhcp) ...

  3. 【MyBatis】学习笔记06:各种查询所返回数据的数据类型

    [Mybatis]学习笔记01:连接数据库,实现增删改 [Mybatis]学习笔记02:实现简单的查 [MyBatis]学习笔记03:配置文件进一步解读(非常重要) [MyBatis]学习笔记04:配 ...

  4. mybatis-plus 使用In查询

    第一种在Dao接口中自定义SQL查询,拼接xml字符串 UserDaoMapper.java @Select("<script>" +"select * fr ...

  5. 如何调整Linux系统为正确时区

    如果你的 Linux 系统时区配置不正确,必需要手动调整到正确的当地时区.NTP 对时间的同步处理只计算当地时间与 UTC 时间的偏移量,因此配置一个 NTP 对时间进行同步并不能解决时区不正确的问题 ...

  6. Qt数据库应用17-通用数据库请求

    一.前言 数据库请求是在数据库采集的基础上,换成http请求的形式来获取数据,这样就脱离了数据库组件,而采用的通用的http请求的形式从数据库拿数据库,这个通用性就非常广泛了,比如对方用java采集到 ...

  7. Qt编写的项目作品20-百度地图综合应用(在线+离线+区域+下载)

    一.功能特点 (一).省市区域地图封装类功能特点 同时支持闪烁点图.迁徙图.区域地图.世界地图.仪表盘等. 可以设置标题.提示信息.背景颜色.文字颜色.线条颜色.区域颜色等各种颜色. 可设置城市的名称 ...

  8. SHA256 64 位加密

    /// <summary> /// SHA256 64位加密 /// </summary> /// <param name="input">&l ...

  9. MySql中MySqlParameter的用法

    在C#中,向表person插入一条数据(表person包括两列:id和name),使用MySqlParameter定义表中各列的值. static void Main(string[] args) { ...

  10. Solution Set -「NOIP Simu.」20221005

    \(\mathscr{A}\sim\)「CF 1252G」Performance Review   Link & Submission.   Tag:「水题无 tag」   记 \(A=a_1 ...