进程:进程是系统资源分配和资源调度的基本单位,每个独立执行的程序在系统中都是一个进程。如qq、word都是一个进程。
线程:线程是进程中的执行流程,一个进程中可以包含多个线程,每个线程也可以得到一个小段程序的执行时间。新起一个线程的方法,可以使用Thread,BackgroundWorker ,ThreadPool,控件.BeginInvoke,委托.BeginInvoke,Timer。
多线程:
- 多线程,指在一个进程下有多个线程。各个线程执行自己的任务,这些线程可以”同时进行“(这里加了双引号,下面会讲述到加双引号的原因)。
- 那多线程有什么好处?多线程应用在生活中随处可见,Word文档就是一个很好的例子。Word有“后台打印”的功能,用户点击打印按钮后,如果发现可以对当前文本进行修改,可以在打印过程中回到主界面进行修改、保存等操作。 如果没有应用多线程,不妨假设用户要打印的文本很长很长,那么用户要等打印操作执行完后,才可以对文本进行修改编辑保存等,这样用户体验就不如多线程的好。还有迅雷,有没有发现迅雷是可以同时下载东西的?例如同时下载A,B,A下载进度到53.4%,B下载进度到47.1%,有时A速度快些,有时B速度快些,反正能确定的是A,B都在下载内容,而不是一定要等A下载完后,B才可以开始下载,这也是多线程的作用。因此,多线程强调”同时,一起进行“,而不是单一的顺下操作。
并发
- Concurrency,是并发的意思。并发的实质是一个物理CPU(也可以多个物理CPU) 在若干道程序(或线程)之间多路复用,并发性是对有限物理资源强制行使多用户共享以提高效率。
- 微观角度:所有的并发处理都有排队等候,唤醒,执行等这样的步骤,在微观上他们都是序列被处理的,如果是同一时刻到达的请求(或线程)也会根据优先级的不同,而先后进入队列排队等候执行。
- 宏观角度:多个几乎同时到达的请求(或线程)在宏观上看就像是同时在被处理。
- 通俗点讲,并发就是只有一个CPU资源,程序(或线程)之间要竞争得到执行机会。第一个阶段,在A执行的过程中B,C不会执行,因为这段时间内这个CPU资源被A竞争到了,同理,第二个阶段只有B在执行,第三个阶段只有C在执行。其实,并发过程中,A,B,C并不是同时在进行的(微观角度)。但又是同时进行的(宏观角度)。
并行
- Parallelism,即并行,指两个或两个以上事件(或线程)在同一时刻发生,是真正意义上的不同事件或线程在同一时刻,在不同CPU资源呢上(多核),同时执行。
- 并行,不存在像并发那样竞争,等待的概念。
- A,B,C都在同时运行(微观,宏观)。
在CPU比较繁忙,资源不足的时候(开启了很多进程),操作系统只为一个含有多线程的进程分配仅有的CPU资源,这些线程就会为自己尽量多抢时间片,这就是通过多线程实现并发,线程之间会竞争CPU资源争取执行机会。
在CPU资源比较充足的时候,一个进程内的多线程,可以被分配到不同的CPU资源,这就是通过多线程实现并行。
至于多线程实现的是并发还是并行?上面所说,所写多线程可能被分配到一个CPU内核中执行,也可能被分配到不同CPU执行,分配过程是操作系统所为,不可人为控制。所有,如果有人问我我所写的多线程是并发还是并行的?我会说,都有可能。
不管并发还是并行,都提高了程序对CPU资源的利用率,最大限度地利用CPU资源。
异步,与同步相对应,是指呼叫另一操作后,不等待其结果,继续执行之后的操作,若之后没有其他操作,当前线程将进入睡眠状态,而CPU时间将有机会切至其他线程。在异步操作完成后通过回调函数的方式获取通知与结果。异步的实现方式有多种,如多线程与完成端口。多线程将异步操作放入另一线程中运行,通过轮询或回调方法得到完成通知;完成端口,由操作系统接管异步操作的调度,通过硬件中断,在完成时触发回调方法,此方式不需要占用额外线程。
1. 利用委托实现异步,lbxx是我扔在前段显示内容的label控件,无须在意
public void AsyncTestByDelegate()
{
//异步
this.lbYB.Text += "线程id:" + Thread.CurrentThread.ManagedThreadId + "<br/>";
Action<Label> action = (Label label) => ReadBook(label);
IAsyncResult asyncResult = null;
AsyncCallback asyncCallback = t => {
this.lbYB.Text += "asyncCallback线程id:" + Thread.CurrentThread.ManagedThreadId + "<br/>";
};
asyncResult = action.BeginInvoke(this.lbYB, asyncCallback, null);
//下面有三种阻塞方式
//while (!asyncResult.IsCompleted)//主线程等待子线程的时候有返回,执行一点就返回一点,可以用来做进度条
//{
// Thread.Sleep(1);
// this.lbYB.Text += "等待线程id:" + Thread.CurrentThread.ManagedThreadId + "<br/>";
//}
//if (asyncResult.AsyncWaitHandle.WaitOne(100))//主线程等待子线程有时间限制,阻塞当前线程100ms,无论是否返回true
//{
// lbYB.Text += "阻塞线程id:" + Thread.CurrentThread.ManagedThreadId + "100ms" + "<br/>";
//}
action.EndInvoke(asyncResult);//主线程等待子线程无返回,一直阻塞,等到完成;EndInvoke根据委托是否有返回值,可以有返回值
lbYB.Text += "线程id:" + Thread.CurrentThread.ManagedThreadId + "完成啦" + "<br/>";
}
public void ReadBook(Label label)
{
label.Text += "ReadBook线程id:" + Thread.CurrentThread.ManagedThreadId + "<br/>"; ;
}
问题来了,如果又要有返回值,又不想阻塞主线程怎么办?
public void AsyncTestWithReturnByDelegate()
{
var stopwatch = Stopwatch.StartNew();
//下面三种方式完全一样
Func<string, string, string> func = (string name, string game) =>
{
Thread.Sleep(5000);
return name + "在玩" + game;
};
func = (string name, string game) => PlayGame(name, game);
func = new Func<string, string, string>(PlayGame);
IAsyncResult iar = func.BeginInvoke("张三", "魂斗罗", PlayGameCallBack, func);//第四个参数可以在回调函数中用IAsyncResult对象的属性AsyncState获取,object类型的,非常灵活,在这里就把委托传了过去。
this.lbYB.Text += "用时" + stopwatch.ElapsedMilliseconds;
}
public string PlayGame(string name, string game)
{
Thread.Sleep(5000);
return name + "在玩" + game;
}
public void PlayGameCallBack(IAsyncResult ar)
{
Func<string, string, string> func = (Func<string, string, string>)ar.AsyncState;//在回调函数中将传过来委托func还原,并调用EndInvoke,这样不会阻塞主线程,也能获得返回值
string r = func.EndInvoke(ar);
}
2. 用线程来实现异步
但是问题来了,上面ReadBook方法是有入参的, Thread t = new Thread(ReadBook); 时,可没有地方给你写入参,这时可以用ParameterizedThreadStart
public void AsyncTestByThread()
{
this.lbYB.Text += "线程id:" + Thread.CurrentThread.ManagedThreadId + "<br/>";
Thread t = new Thread(new ParameterizedThreadStart(UseReadBook));
t.IsBackground = true;
t.Start(this.lbYB);//入参
t.Join();//表示把thread线程任务join到当前线程,也就是当前线程等着thread任务完成
}
public void UseReadBook(object ojb)
{
duoxiancheng dxc = new duoxiancheng();
dxc.ReadBook((Label)ojb);
}
如果有多个入参呢,很简单,UseReadBook的入参是object,可以写个辅助类,多个参数作为类的属性就行了呀。
如果有返回值呢?用委托的话可以用EndInvoke接受返回值,Thread也可以用辅助类返回,但是比较麻烦,我选择用委托。