进程
计算机概念,程序在服务器运行时占据全部计算机资源总和,虚拟的。包含CPU、内存、网络、硬盘
MSDN:
当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。
而一个进程又是由多个线程所组成的。
线程
计算机概念,进程在响应操作时最小单位,也包含CPU、内存、网络、硬盘
MSDN:
线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。
多线程
计算机概念,一个进程有多个线程同时运行。
MSDN:
多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
为什么可以多线程
多个CPU的核可以并行工作,
4核8线程,这里的线程指的是模拟核
CPU分片
1s的处理能力分成1000份,操作系统调度着去响应不同的任务
从宏观角度来说,就是多个任务在并发执行
从微观角度来说,一个物理cpu同一时刻只能为一个任务服务
并行:多核之间叫并行
并发:CPU分片的并发
多线程其实是资源换性能,1 资源不是无限的 2 资源调度损耗
多线程的好处:
可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以 运行其它的线程而不是等待,这样就大大提高了程序的效率。
多线程的不利之处:
线程也是程序,所以线程需要占用内存,线程越多占用内存也越多; 多线程需要协调和管理,所以需要CPU时间跟踪线程;
线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;线程太多会导致控制太复杂,最终可能造成很多Bug;
同步与异步
同步:
发起调用,完成后才继续下一行;非常符合开发思维,有序执行;
异步:
发起调用,不等待完成,直接进入下一行,启动一个新线程来完成方法的计算
同步方法有序进行,异步多线程无序
启动无序:线程资源是向操作系统申请的,由操作系统的调度策略决定,所以启动顺序随机。同一个任务同一个线程,执行时间也不确定,CPU分片
以上相加,结束也无序
同步方法慢,异步多线程方法快
同步:只有一个线程计算 异步:因为多个线程并发计算
同步方法卡界面,异步多线程方法不卡界面
同步方法: 主线程(UI线程)忙于计算,无暇他顾
异步多线程方法: 异步多线程方法不卡界面:主线程闲置,计算任务交给子线程完成
同步代码示例:
/// <summary> /// 同步方法 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { Console.WriteLine($"button1_Click*****Start{Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString()}"); for (int i = 0; i < 5; i++) { string name = string.Format($"button1_Click{i}"); this.DoSomethingLong(name); } Console.WriteLine($"button1_Click*****End{Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToString()}"); }
程序执行完成,我们可以清晰的看到,同步方法是有序的执行
委托异步多线程代码示例:
/// <summary> /// 异步多线程 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click_1(object sender, EventArgs e) { Console.WriteLine($"****************button2_Click Start " + $"{Thread.CurrentThread.ManagedThreadId.ToString("00")} " + $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************"); Action<string> action = this.DoSomethingLong; for (int i = 0; i < 5; i++) { string name = string.Format($"button2_Click_1{i}"); action.BeginInvoke("button2_Click", null, null);//委托异步多线程调用 } Console.WriteLine($"****************button2_Click End " + $"{Thread.CurrentThread.ManagedThreadId.ToString("00")} " + $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************"); }
程序运行之后,我们会发现运行的结果是无序的
委托异步多线程 回调
将后续动作通过回调参数传递进去,子线程完成计算后,去调用这个回调委托。
代码示例:
/// <summary> /// 异步多线程回调、等待 /// Action /// Func /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button3_Click(object sender, EventArgs e) { #region 回调 { Action<string> action = this.DoSomethingLong; IAsyncResult asyncResult = null;//是对异步调用操作的描述 AsyncCallback callback = mm => {//这里是一个委托 Console.WriteLine($"{object.ReferenceEquals(mm, asyncResult)}"); Console.WriteLine($"button3_Click计算成功了。{mm.AsyncState}。{Thread.CurrentThread.ManagedThreadId.ToString("00")}"); }; //回调 action.BeginInvoke("button3_Click", callback, "嘿嘿");//先执行action然后回调 callback委托 } #endregion }
委托异步多线程等待的三种方式:
IsComplate等待
通过IsComplate等待,主线程在等待,边等待边提示
#region IsComplate等待 { Action<string> action = this.DoSomethingLong; IAsyncResult asyncResult = null;//是对异步调用操作的描述 AsyncCallback callback = mm => { Console.WriteLine($"{object.ReferenceEquals(mm, asyncResult)}"); Console.WriteLine($"button3_Click计算成功了。{mm.AsyncState}。{Thread.CurrentThread.ManagedThreadId.ToString("00")}"); }; //回调 asyncResult= action.BeginInvoke("button3_Click", callback, "嘿嘿");//先执行action然后回调 callback委托 int i = 0; while (!asyncResult.IsCompleted) { Thread.Sleep(200); if (i < 2) { Console.WriteLine($"正在充电{++i * 10}%...."); } else{ Console.WriteLine($"充电完成完成99.999999%...."); } } Console.WriteLine("充电已完成,尽情的使用手机吧!"); } #endregion
程序执行完成的结果
注意:根据调用,首先执行DoSomethingLong 方法,因为DoSomethingLong方法里有密集操作,所以程序会边等待边执行while里的判断语句以及方法体
此时DoSomethingLong里的密集操作已执行完毕,接着执行callback里的方法
WaitOne等待
WaitOne等待,即时等待 限时等待
asyncResult.AsyncWaitHandle.WaitOne();//直接等待任务完成
asyncResult.AsyncWaitHandle.WaitOne(-1);//一直等待任务完成
asyncResult.AsyncWaitHandle.WaitOne(1000);//最多等待1000ms,超时就不等了
EndInvoke等待
即时等待,而且可以获取委托的返回值 一个异步操作只能End一次
#region EndInvoke Func<int> func = ()=> { Thread.Sleep(2000);
return DateTime.Now.Hour; }; int Result = func.Invoke(); IAsyncResult asyncResult = func.BeginInvoke(ar=> { //在方法里拿到结果 // int IResult = func.EndInvoke(ar); //对于每个异步操作,只能调用一次EndInvoke }, null); //在方法外得到结果 int EndResult = func.EndInvoke(asyncResult); #endregion