先需要讲解一下计算机的基本概念:
进程:一个程序在操作系统上运行时,占用操作系统的资源,起名叫进程。
线程:线程是程序执行的最小单位,进程在执行任何一个操作或者响应,都是由线程完成的,执行过程中,使用到操作系统的资源合集,就叫线程,进程可以包含多个线程或者单单一个主线程(也称作UI线程),线程一定属于进程。
多线程:在操作系统里,一个进程里面有多个执行流并发执行。在C#里Thread是.Net封装的一个类,来对应上操作系统上的多线程。
using System;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.SyncCal();
p.AsyncCal();
Console.ReadKey();
}
//同步方式
private void SyncCal()
{
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start(); //开始监视代码运行时间
Console.WriteLine("同步计算方法开启,当前线程的ID是{0}", Thread.CurrentThread.ManagedThreadId);
for (int i = 0; i < 5; ++i)
{
string name = string.Format("{0}_{1}", "SyncCal", i);
this.DoSomething(name);
}
Console.WriteLine("同步计算方法结束,当前线程的ID是{0}", Thread.CurrentThread.ManagedThreadId);
watch.Stop(); //停止监视
TimeSpan timespan = watch.Elapsed; //获取当前实例测量得出的总时间
Console.WriteLine("打开窗口代码执行时间:{0}(毫秒)", timespan.TotalMilliseconds); //总毫秒数
}
//异步方式
private void AsyncCal()
{
Console.WriteLine("异步计算方法开启,当前线程的ID是{0}", Thread.CurrentThread.ManagedThreadId);
Action<string> func = this.DoSomething;
//func.Invoke("AsyncCal");//同步调用
for (int i = 0; i < 5; ++i)
{
string name = string.Format("{0}_{1}", "AsyncCal", i);
func.BeginInvoke("AsyncCal", null, null);//异步调用
}
Console.WriteLine("异步计算方法结束,当前线程的ID是{0}", Thread.CurrentThread.ManagedThreadId);
}
public void DoSomething(string name)
{
//System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
//watch.Start(); //开始监视代码运行时间
Console.WriteLine("当前操作={0}当前线程的ID={1}当前的时间={2}", name,
Thread.CurrentThread.ManagedThreadId.ToString("00"), DateTime.Now.ToString("HHmmss:fff"));
long lResult = 0;
for (int i = 0; i < 1000000000; i++)
lResult += i;
Console.WriteLine("当前操作={0}当前线程的ID={1}当前的时间={2}计算出来的值={3}", name,
Thread.CurrentThread.ManagedThreadId.ToString("00"),
DateTime.Now.ToString("HHmmss:fff"),lResult);
//watch.Stop(); //停止监视
//TimeSpan timespan = watch.Elapsed; //获取当前实例测量得出的总时间
//Console.WriteLine("打开窗口代码执行时间:{0}(毫秒)", timespan.TotalMilliseconds); //总毫秒数
}
}
}
从以上代码段可以发现同步发起调用时,必须等着完成之后,才能进入到下一行代码段。异步发起调用,不用等待完成,直接进入下一行代码段,原来的操作也会有新的操作流去完成。但是从运行结果也可以发现一些特点:
第一点就是正常来说开启5个线程执行速度应该是同步执行的5倍,应该是接近线性的,其实不然可以看到电脑只开启了4个线程(和CPU的核心数有关),最快结束的线程再去做第五项工作,也就是说这个线程ID是6的线程它做了2次计算,而且线程6结束到下次开启线程6做第5次计算的时候也有间隔,这样说明影响速度的原因是资源有限和线程调度花费导致不可能是线性倍数的,当开启多线程的时候可以在任务管理器发现CPU占用突然变的很高。
第二点就是同步的时候,如果是Winform程序,会发现这个程序会出现假死的状态,就是不能拖动和关闭等等,但是异步多线程可以随意操作窗口,主要原因是同步的时候主线程(UI线程)还在计算不能处理窗口响应也就是卡死了,而异步多线程运行的时候,主线程是空闲的,所以不会出现假死的情况,这个时候同步就看上去十分的不友好,在处理大型文件下载和Unity的AB包加载等等耗时操作的时候可以开启异步多线程,而主线程可以提示任务进度和响应一些操作(这里我需要吐槽一下,以前接SDK的时候,放在线程里调用SDK获取短信验证码的时候一直闪退,后面才知道不能再非UI线程对UI进行操作,之后放在UI线程里面调用这个函数,问题就解决了)
第三点异步多线程的无序性,启动时候无序没有阻塞的,同一时间申请线程,操作系统是无法掌控的,执行时间不确定,因为CPU的调度是操作系统完成的(线程优先级也只是影响分配策略),结束也是无序的,先启动的不一定先结束,所以多线程很难控制。