第二阶段WinForm-10:多线程
1_进程
(1)进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,【是系统进行资源分配的基本单位】,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,【进程是线程的容器】。程序是指令、数据及其组织形式的描述,进程是程序的实体。
(2)进程是程序运行的环境。进程是线程的容器。一个进程可以包含多个线程。
进程是系统进行资源分配的基本单位,而线程是系统进行资源分配的最小单位(线程不能再分)
2_线程
2.1_线程的概念
(1)线程(Thread)是操作系统能够进行【运算调度的最小单位】。它被包含在进程之中,是进程中的实际运作单位。【一条线程指的是进程中一个单一顺序的控制流】,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
概括:线程是操作系统资源调度的最小单位。线程不能独立运行,必须包含在进程中。进程中可以包含多个线程。多线程执行时是并行(异步),无序的。
(2)串行——>排队——>阻塞(同步) 并行——>无序——>非阻塞(异步)
线程之间要通讯。会有两种方案:同步和异步。
单个线程中能不能异步?可以;【多线程肯定是异步。异步不一定是多线程。】
多线程中有N个线程,但主线程只有一个,其他的线程都称为分线程。对于单线程程序来说,程序中只有一个线程,这个线程就是主线程。
2.2_多线程
(1)多线程是指程序中包含多个执行流(线程),即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
概括:多线程让程序同时运行多个线程,多个线程并行执行(执行时无顺序)
(2)优点:可以提高CPU的利用率,大大提高程序的运行效率,增强用户体验
缺点:
-
线程也是程序,所以线程运行需要占用计算机资源,线程越多占用资源也越多。(占内存多)
-
多线程需要协调和管理,所以需要CPU跟踪线程,消耗CPU资源。(占cpu多)
-
线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题。(多线程存在资源共享问题 锁)
-
线程太多会导致控制太复杂,最终可能造成很多Bug。(管理麻烦,容易产生bug)
(3)死锁:某个资源不能空闲
2.3_分线程的创建
(1)完整写法: 创建线程——> 传入委托类型的变量——>委托保存函数——>函数就是执行耗时任务的函数
//1.实例化一个分线程//public delegate void ThreadStart(); 不带参数的委托ThreadStart threadStart1 = new ThreadStart(ThreadFun1);Thread thread1 = new Thread(threadStart1);public void ThreadFun1(){Console.WriteLine("分线程1");}
(2)简写1:实例化一个委托,将方法直接给委托,将委托传入线程进行创建
//2.实例化一个分线程
//public delegate void ThreadStart(); 不带参数的委托
ThreadStart threadStart1 = ThreadFun1;
Thread thread1 = new Thread(threadStart1);public void ThreadFun1(){Console.WriteLine("分线程1");}
(3)简写2:使用Lambda表达式创建委托,将委托传入线程进行创建
//3.实例化一个分线程//public delegate void ThreadStart(); 不带参数的委托ThreadStart threadStart1 = new ThreadStart(() =>{Console.WriteLine("分线程1");});Thread thread1 = new Thread(threadStart1);
(4)简写3:将函数直接传入线程的实例化进行创建,注意方法要满足ThreadStart委托(无参无返回值)或ParameterizedThreadStart委托(有一个object对象的参数),推荐写法
//4.实例化一个分线程//推荐写法 省略委托,直接写入函数 需要注意满足ThreadStartThread thread1 = new Thread(ThreadFun1);public void ThreadFun1(){Console.WriteLine("分线程1");}
(5)使用Lambda表达式创建线程,耗时操作逻辑代码比较少的时候使用
//5.实例化一个分线程 Thread thread1 = new Thread(() =>{Console.WriteLine("分线程1");});
2.4_向分线程中传数据
(1)使用ParameterizedThreadStart委托,ParameterizedThreadStart委托类型 可以保存一个 有参数 没有返回值的委托
(2)完整写法:创建ParameterizedThreadStart委托,并传入线程的创建
ParameterizedThreadStart parameterizedThreadStart = new ParameterizedThreadStart(s =>{ThreadFun2(s);});
Thread thread = new Thread(parameterizedThreadStart);thread.Start(message);public void ThreadFun2(object o){Console.WriteLine("分线程2");
Console.WriteLine(o);
//message
}
(3)简写1:使用Lambda表达式创建线程,并在表达式里面调用方法
//写法2: 向线程传参(好一些,但是没有parameterizedThreadStart委托,而是使用的ThreadStart委托)Thread thread = new Thread(() =>{ThreadFun2(message);});
thread.Start();public void ThreadFun2(object o){Console.WriteLine("分线程2");
Console.WriteLine(o);
//message
}
(4)简写(推荐):将有一个object类型的参数无返回值的函数传入线程的创建,在线程启动的时候传入参数
//写法3:建议使用(省略parameterizedThreadStart,通过Start传递参数)
Thread thread = new Thread(ThreadFun2);
thread.Start(message);
public void ThreadFun2(object o)
{Console.WriteLine("分线程2");Console.WriteLine(o);
}
(5)注意:向线程传入的方法就是分线程需要执行的逻辑代码,即函数就是执行耗时任务的函数。
2.5_Thread类中的属性和方法
(1)Thread 类 专门用于创建(实例化)和管理(启动 停止)线程
启动分线程 在分线程启动的时候,会执行分线中的业务逻辑,但是主线程中的业务逻辑 也在执行,即分线程和主线程同时执行(并行执行,无序的)
(2)2.5.2_属性:
属性 | 描述 |
---|---|
CurrentContext | 获取线程正在执行的上下文 |
CurrentCulture | 获取或设置当前线程的区域性 |
CurrentPrincipal | 获取或设置线程的当前负责人(对基于角色的安全性而言) |
CurrentThread | 获取当前正在运行的线程 |
CurrentUICulture | 获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源 |
ExecutionContext | 获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息 |
IsAlive | 获取当前线程的执行状态 如果返回true 表示线程还"活着" false 表示 线程"结束" |
IsBackground | 获取或设置一个值,该值表示某个线程是否为后台线程 |
IsThreadPoolThread | 获取线程是否属于托管线程池 |
ManagedThreadId | 获取当前托管线程的唯一标识符 |
Name | 获取或设置线程的名称 |
Priority | 获取或设置线程的调度优先级 |
ThreadState | 获取当前线程的状态 |
2.5.3_方法:
方法名 | 描述 |
---|---|
Start() | 启动 |
Suspend() | 暂停 |
Resume() | 继续 |
Abort() | 停止 |
Join | 阻塞线程 |
Thread.ResetAbort() | 取消当前线程请求的 Abor |
2.5.4_ThreadState 线程状态
值 | 描述 |
---|---|
Unstarted | 线程已创建但尚未调用Start() 方法 |
Running | 线程正在正常运行 |
StopRequested | 线程正在被请求停止(已过时,不建议使用) |
Suspended | 线程已被挂起(已过时,不建议使用) |
SuspendRequested | 正在请求线程挂起(已过时,不建议使用) |
Background | 线程作为后台线程运行 |
WaitSleepJoin | 线程因调用Wait() , Sleep() 或Join() 而阻塞 |
Aborted | 线程已中止,但尚未停止 |
AbortRequested | 已对线程调用Abort() ,但线程尚未中止 |
Stopped | 线程已停止 |
2.4.5_ThreadPriority线程优先级
优先级有时候会失效
ThreadPriority属性 | 值 |
---|---|
Lowest | 线程优先级最低,系统会优先调度其他更高优先级的线程。 |
BelowNormal | 线程优先级略低于正常级别,但高于最低优先级。 |
Normal | 默认优先级,线程与大多数应用程序线程同级调度。 |
AboveNormal | 线程优先级略高于正常级别,但低于最高优先级。 |
Highest | 线程优先级最高(但不保证实时性),系统会优先调度。 |
2.4.6_IsBackground
IsBackground 默认的值是false 前台线程 主线程关闭的时候,前台线程不会自动关闭
设置为true表示是后台线程,建议带参数的线程设置成后台线程,这样在主线程关闭的时候,开发者不再需要操心后台线程是否关闭,主线程关闭,后台线程一并关闭
2.6_一个线程执行多个耗时任务
private void button12_Click(object sender, EventArgs e)
{CallbackMultOperate(() => {Console.WriteLine("耗时任务1");int sum = 0;for (int i =0;i<10000;i++){sum += i; }Invoke(new Action(() =>{label2.Text = sum.ToString();}));},() => {Console.WriteLine("耗时任务1");int sum = 0;for (int i = 0; i < 10000; i++){sum += i;}Invoke(new Action(() =>{label3.Text = sum.ToString();}));});
}
public void CallbackMultOperate(Action action1, Action action2){//一个线程执行多个耗时任务Thread t =new Thread(() =>{action1.Invoke();//执行第一个耗时任务action2.Invoke();//执行第二个耗时任务});t.Start();}
2.7_线程锁
一个资源锁住之后,只能被某个线程使用,其他的线程等待
目的: 增强资源的安全性 什么时候使用锁:防止一个资源在多个线程中被同时修改
建议阅读: C# 锁 Lock 、Monitor 、Mutex 、ReaderWriterLockSlim - 不争丶 - 博客园 多线程学习 各种锁类型与对比 lock_c# 线程锁有哪几种-CSDN博客
static object obj = new object();//创建一个锁 互斥锁
lock (obj)
{num++;Console.WriteLine("线程1中的num" + num);
}
3_多线程问题(面试题)
(1)在分线程中,为什么不能操作UI(界面)?
-
UI都是从从主线程创建的 C#不允许跨线程访问
(2)跨线程调用资源是不允许的,如何解决?
-
对非法的跨线程调用不进行检测(允许跨线程调用) CheckForIllegalCrossThreadCalls = false;
Check 检验 For 为了 Illegal 非法的 CrossThreadCalls跨线程访问
-
把耗时任务放在分线程中执行,而页面的更新放到主线程中
Invoke:在控件所在的线程(即主线程)执行一个委托
Invoke(new Action(() =>{ label1.Text = o.ToString(); }));
(4)开发者想让分线程中的代码执行完毕,再执行主线程中的代码,怎么办?
-
Join() 可以让主线程等待分线程的结果[平时少用] 因为多线程就是为了解决阻塞问题,此处等待无意义
4_线程池
(1).NET Framework2.0时代,出现了一个线程池ThreadPool,是一种池化思想,如果需要使用线程,就可以直接到线程池中去获取直接使用,如果使用完毕,在自动的回放到线程池去;
简单来说就是:.net 2.0出现线程池,线程池中可以存在多个线程,让线程池来自动管理(垃圾回收器GC和公共语言运行时CLR)。
(2)好处:解决了部分Thread管理不便的问题,移除了无用的Thread API。提高线程运行性能。
(3)重要API:QueueUserWorkItem(Callback,data)
开启线程
ThreadPool.QueueUserWorkItem(state=> {Console.WriteLine(state);}, "abc1");
ThreadPool.QueueUserWorkItem(state => {Console.WriteLine(state);}, "abc2");
ThreadPool.QueueUserWorkItem(state => {Console.WriteLine(state);}, "abc3");
等待线程
//等待事件
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(state => {//分线程的代码是有序的,线程和其他的线程执行是无序的,想有序借助优先级DoSomething(state.ToString());manualResetEvent.Set();}, "吴亦凡");
manualResetEvent.WaitOne();//阻塞 等待Set() 执行 才执行一下的代码
Console.WriteLine("AAAAA");
private void DoSomething(string s)
{Thread.Sleep(1000);Console.WriteLine(s);
}