多线程,并发,并行,异步与同步


进程:进程是系统资源分配和资源调度的基本单位,每个独立执行的程序在系统中都是一个进程。如qq、word都是一个进程。

线程:线程是进程中的执行流程,一个进程中可以包含多个线程,每个线程也可以得到一个小段程序的执行时间。新起一个线程的方法,可以使用Thread,BackgroundWorker ,ThreadPool,控件.BeginInvoke,委托.BeginInvoke,Timer。

多线程:

  1. 多线程,指在一个进程下有多个线程。各个线程执行自己的任务,这些线程可以”同时进行“(这里加了双引号,下面会讲述到加双引号的原因)。
  2. 那多线程有什么好处?多线程应用在生活中随处可见,Word文档就是一个很好的例子。Word有“后台打印”的功能,用户点击打印按钮后,如果发现可以对当前文本进行修改,可以在打印过程中回到主界面进行修改、保存等操作。 如果没有应用多线程,不妨假设用户要打印的文本很长很长,那么用户要等打印操作执行完后,才可以对文本进行修改编辑保存等,这样用户体验就不如多线程的好。还有迅雷,有没有发现迅雷是可以同时下载东西的?例如同时下载A,B,A下载进度到53.4%,B下载进度到47.1%,有时A速度快些,有时B速度快些,反正能确定的是A,B都在下载内容,而不是一定要等A下载完后,B才可以开始下载,这也是多线程的作用。因此,多线程强调”同时,一起进行“,而不是单一的顺下操作。

并发

  1. Concurrency,是并发的意思。并发的实质是一个物理CPU(也可以多个物理CPU) 在若干道程序(或线程)之间多路复用,并发性是对有限物理资源强制行使多用户共享以提高效率。
  2. 微观角度:所有的并发处理都有排队等候,唤醒,执行等这样的步骤,在微观上他们都是序列被处理的,如果是同一时刻到达的请求(或线程)也会根据优先级的不同,而先后进入队列排队等候执行。
  3. 宏观角度:多个几乎同时到达的请求(或线程)在宏观上看就像是同时在被处理。
  4. 通俗点讲,并发就是只有一个CPU资源,程序(或线程)之间要竞争得到执行机会。第一个阶段,在A执行的过程中B,C不会执行,因为这段时间内这个CPU资源被A竞争到了,同理,第二个阶段只有B在执行,第三个阶段只有C在执行。其实,并发过程中,A,B,C并不是同时在进行的(微观角度)。但又是同时进行的(宏观角度)。

并行

  1. Parallelism,即并行,指两个或两个以上事件(或线程)在同一时刻发生,是真正意义上的不同事件或线程在同一时刻,在不同CPU资源呢上(多核),同时执行。
  2. 并行,不存在像并发那样竞争,等待的概念。
  3. 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也可以用辅助类返回,但是比较麻烦,我选择用委托。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM