多线程&网络编程(异步编程)
1)重要性, 高并发, 短时间内遇到大量请求 2)难度 硬件.操作系统 多线程本身的复杂性, 死锁 ,资源抢占, 线程同步...
--->多线程 进程:一般指程序中运行的程序,实际作用是为程序在执行过程中创建好所需要的环境和资源. 线程:是进程的一个实体,是Cpu用来调度执行程序的最小单元 ,一个进程可以拥有多个线程.
并发:一段时间内 同时做多件事情.
本质上没有并发执行,只能执行一个线程,到那时多个线程被快速的交替执行
使得在宏观上有了多个线程快速的交替执行.
并行:同一时刻做很多件事情
多个线程在多个处理器同时执行 无论是宏观还是微观
在单处理器下,多线程一定是并发执行的.
多处理器下
线程个数<=处理器个数,多线程是并行的.
线程个数>=处理器个数,多线程是并发&并行
同步&异步 同步 :等待前一个任务结束后,再执行下一个任务.无缤纷噶/并行概念 异步 :多个任务同时执行,并发&并行 模型核心:任务的拆分 Thread 的start方法不是马上开启线程执行的意思,是给线程做了一个标记, 告诉CPU 我已经准备OK了随时可以执行 具体什么时候执行还得看CPU
带参数的多线程 以及开发中经常用到的2方法 Sleep 睡的是当前的线程 如何让子线程阻塞主线程的运行呢 .Join() 阻塞此线程所在的线程,直到此线程执行完毕
也可以通过传值 来发生装箱操作保证值不变
多线程优先级 Priority优先级么有用了,因为现在都是多核心CPU 如果在一个CPU上运行就有用了 枚举类型
线程的生命周期 5个大状态, 1)创建了线程对象,但是并没有执行Start(),new Thread();线程对象.start() 出生状态 2)调用Start ,将线程标记为可执行状态. 就绪状态. 3)执行中 执行状态 4)Join/sleep 暂停掉/停止掉 只要在运行的过程中,线程被暂停掉了,就称之为暂停状态 5)结束啦, 结束状态.
ThreadState.Aborted 结束掉线程 (非正常的结束状态 --夭折) AbortedRequested请求结束线程. Background 后台线程. 程序默认开的所有的线程都是前台的.前台是只有所有的前台线程结束,程序才会结束. 后台线程 , 只要所有的前天线程结束了,不管当前这个后台线程结束与否都会自动结束 一般把一些不重要的事情放在后台线程里 Running 正在执行的状态 Stopped 线程结束的状态 Suspended 线程挂起的状态, 一般不推荐使用 容易引起死锁. unstarted 没有开始的状态 出生状态 Wait Sleep Join 调用了Sleep/Join 的线程状态.
Aborted结束掉线程(非正常的结束 ) 伪结束并不是真正的结束 会抛异常 夭折 结束后不是正常的stopped的状态 Interrupt();打断线程的执行,后续可以继续执行. 会抛异常 打断
如果设置了后台线程,在线程处于正常状态下我们看到的就是background
多线程的同步
多线程同步(并发编程)三大特征 可见性:当多个线程访问同一个变量是,一个线程修改了这个变量的值,其他线程能够立即看到修改后的值 希望是但是实际上不是 原子性:即执行一个操作或者多个操作(执行的过程中不被任何因素打断) 要么全部执行,要么都不执行 1)cpu去内存中读取变量值 2)操作变量的值 3)把值重新写入内存. 此三项要么一套 全部执行 要么全部不执行 希望是 有序性:程序的执行按照代码的先后顺序执行. 程序被编译成IL中间代码 --->二进制 这个时候,可能会出现CPU由于自己的底层算法,可能不按写代码的顺序执行 希望是
解决第一个问题 volatile 可见性问题 用volatile 修饰变量 意思是不要操作寄存器了 直接去内存中操作 从而改变了可见性 有序性 因为上述方法无法保证原子性,所以需要加锁保证完整性 Monitor .enter(o);拿到锁 互斥锁 monitor.exit(o);释放锁 lock(o) 语法糖 实际上是编译后楼上的方法 { }
如果想让一个整数类型保证原子性的操作的时候
Interlocker.各种方法(ref);就可以 不要volatile lock都可以
volatile 锁一个变量
lock 锁一个代码块
怎么锁一个方法呢
标记 : [MethodImpl(MethodImplOptions.Synchronized)]
死记硬背 锁定一个方法,保证同一时刻只能有一个线程进入方法
线程安全/线程不安全 安全:一段代码被多个线程执行的时候,不会出乱子,结果是我们想要的结果 不安全:一段代码被多个线程执行的时候出乱子,结果不是我们想要的结果
需要写安全的程序.
信号量控制线程. 通过信号量可以轻松灵活的控制多线程的执行 可以理解为信号灯. ManualResetEvent manualResetEvent = new ManualResetEvent(false); manualResetEvent.WaitOne();//不让往下执行,等待信号
两个搭配使用 问题是 manualResetEvent.Set();一次之后就往后都可以执行了 不需要等待了 满足条件 重新设置 manualResetEvent.Reset();
ManualResetEvent
AutoResetEvent 区别 waitOne之后会自动的调一下Reset 方法
自动的点
死锁
互斥性:当一个资源被线程使用的时候,别的资源是不能使用的. 不可抢占性:资源请求者,不可强制从资源拥有者中抢夺资源 占有且等待性:资源请求者在等待其他资源时,保持对原有资源的占有 循环等待性:线程1等待线程2 占有的资源 线程2 等待线程1 占有的资源
语法层面上避免死锁:代码中尽量不要出现锁套锁的情况.只要出现了 锁套锁就有可能形成死锁 如果非要这么锁就 尽量按照顺序锁.
四个特性 前三个基本都不能动,因为要线程同步 要保证结果的稳定性\ 能做的 只能是破坏第四个条件,也就是我们的程序中一定不要形成循环等待的回路 要从根源上避免这种情况的发生.
线程池
创建多线程时消耗系统资源的 多线程在执行的时候也是消耗系统资源的 存储线程状态上下文
线程池,就是存储线程的池子, 里面是一组已经创建好的线程,随用随取 用完了不销毁 供给后续使用.
ThreadPool 默认线程个数跟cpu核数有关 几个CPU就是有几个线程 如果不够也不用担心 平台会自动弄新的线程加进去 线程的创建和销毁都由线程池自己来搞定 可以帮助我们节省系统资源 问题:没有办法精确的控制线程, ThreadPool.QueueUserWorkItem((obj) => { }); 节约了创建线程时候的系统资源 也节约了线程运行时候的上下文资源
异步编程模型 EAP基于事件的异步模型 优点:使用简单 缺点:不太好处理复杂的业务逻辑 支持它相关的模型类不多, WebClient 基于事件来实现, 下载前注册一个下载结束后的事件,当事件被触发执行说明我们的下载任务完成了
APM: 异步模型 基于异步结果的 IAsyncResult 返回值 AsyncWaitHandle 一个用来等待异步结束后的同步对象 APM 异步编程模型,一般都以BeginXXX开始,EndXXX结束 begin 表示准备开始XXXX 什么时候执行 看CPU 调度,
所以要用 asyncResult.AsyncWaitHandle.WaitOne(); 卡住
优点:C#中支持的类比较多 Socket ,Stream等...
缺点:麻烦!
由于有waitone 的存在会卡死当前线程
所以要开线程池使用
TPL: 异步模型 最常用 基于任务的异步编程模型 多线程问题:编写繁琐.多线程同步问题. 抢占资源问题 调度问题,同步问题,死锁问题,浪费资源... 线程池:现成的抽象封装.操作简单 节省资源.问题 获取线程执行结果 ,相关的连续异步操作,会很复杂. 线程池:现成的抽象封装,操作简单,节省资源 发挥多核CPU的功效,提升程序整体的运行性能,不需要编写底层复杂 且逻辑复杂的多线程代码
核心: Task async await
Task:什么是任务,编程中做的每个事情,都是一个任务
创建任务的三种方法
1)以创建对象的方式创建task
Task task = new Task(() => Test("TASK1") );
task.start();
**此方式需要手动start 执行任务
任务 是被线程来执行的 ,虽然没有手动的开启线程,但是也被线程池中的线程执行了
所以任务封装了线程相关操作的代码.
2)通过.RUN方法来创建
3)通过.Factory.StartNew来创建
2.3 两种方式都不需要手动去start
task 创建的任务都是线程池里的线程执行的
如何获取任务的返回值
两种方法
1)task.Result属性
使用的是线程池里的线程
2).RunSynchronously方法.
此方法是同步的调度任务执行,在主线程中同步执行,保证结果的稳定性
一般计算量小 数据需要精准,就同步执行 对程序运行速度影响小
使用的是主线程
控制任务的执行顺序.
task.Wait();
等待上一个任务执行完成并且会阻塞主线程,
Task.WaitAll() 参数是可变的task数组,等待所有task 执行完了在执行下面的代码
Task.WaitAny();
标签:异步,多线程,结束,编程,线程,执行,CPU
From: https://blog.51cto.com/u_16228222/7112198