应无所住,而生其心
排名
1
文章
858
粉丝
112
评论
163
net core webapi post传递参数
庸人 : 确实坑哈,我也是下班好了好几次,发现后台传递对象是可以的,但...
百度编辑器自定义模板
庸人 : 我建议换个编辑器,因为现在百度富文本已经停止维护了,用tinymec...
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术

等待所有线程执行完后,在执行某个方法

13823人阅读 2019/4/22 22:24 总访问:5166910 评论:3 收藏:1 手机
分类: 多线程

彼年豆蔻,谁许谁地老天荒。



在多线程环境下,我们可能会需要等待开辟的线程执行完后,再去执行某个方法,例如输出并行计算结果等。

但是在多线程下,线程的执行是不阻塞主线程的,这点其实也是多线程的优势,提高代码执行效率,不必相互等待可以并行执行

例如如下代码:

  1.    private void button1_Click(object sender, EventArgs e)
  2.    {
  3.          for (int i = 0; i < 3; i++)
  4.      {
  5.              Task task = new Task((obj) =>
  6.              {
  7.                 System.Threading.Thread.Sleep(500);
  8.                 MessageBox.Show(obj + "");
  9.              },i);
  10.              task.Start();
  11.       }
  12.  
  13.             MessageBox.Show("线程执行完毕了");      
  14.    }

 当点击按钮开启线程后,先弹出来的不是开启的线程弹窗,而是主线程的线程弹窗。


 现在我们来通过某些方法,实现可以在多线程执行完毕后在执行自己的逻辑

 几种常用的处理方法


1:使用Task.WaitAll (会阻塞主线程)

可以使用Task.WaitAll让主线程一直等待开辟的线程,直到所有子线程都执行完后再执行主线程逻辑

  1.  
  2.         private void button1_Click(object sender, EventArgs e)
  3.         {
  4.             Task[] tasks = new Task[3];
  5.  
  6.             for (int i = 0; i < 3; i++)
  7.         {
  8.                 Task task = new Task((obj) =>
  9.                 {
  10.                     System.Threading.Thread.Sleep(500);
  11.                     MessageBox.Show(obj + "");
  12.                 },i);
  13.  
  14.                 tasks[i] = task;
  15.                 task.Start();
  16.         }
  17.  
  18.             Task.WaitAll(tasks);
  19.             MessageBox.Show("线程执行完毕了");      
  20.         }

Task.WaitAll 是一个等待的过程,一旦全部执行完毕了,就继续往下执行,这里是阻塞的


 2:使用Task.WhenAll(不会阻塞主线程)


Task.WhenAll和Task.WaitAll类似都可以做到线程执行后在向下执行,但是wait是等待也就是会阻塞主线程,而when表示当的意思,就是当子线程都执行完后在执行一个回调函数。可以把子线程执行完毕后想执行的代码放入该回调函数里边。

  1.      private void button1_Click(object sender, EventArgs e)
  2.         {
  3.             Task[] tasks = new Task[3];
  4.  
  5.             for (int i = 0; i < 3; i++)
  6.             {
  7.                 Task task = new Task((obj) =>
  8.                 {
  9.                     System.Threading.Thread.Sleep(2000);
  10.                     MessageBox.Show(obj + "");
  11.                 }, i);
  12.  
  13.                 tasks[i] = task;
  14.                 task.Start();
  15.             }
  16.  
  17.             Task.WhenAll(tasks).ContinueWith(a =>
  18.             {
  19.                 MessageBox.Show("线程执行完毕了");
  20.             });
  21.             MessageBox.Show("主线程被执行了");
  22.         }


3:使用Parallel.Invoke或者  Parallel.For(会阻塞主线程)

Parallel.Invoke或者  Parallel.For可以很方便的开辟多线程并行执行,自动会阻塞多线程。所以他后面写的代码会

自动等待子线程执行完毕后再执行

  1.    private void button2_Click(object sender, EventArgs e)
  2.         {
  3.             //开辟子线程,并行执行
  4.             Parallel.Invoke(() =>
  5.             {      
  6.                 System.Threading.Thread.Sleep(1500);
  7.                 MessageBox.Show("线程A");
  8.             }, () =>
  9.             {
  10.  
  11.                 System.Threading.Thread.Sleep(1500);
  12.                 MessageBox.Show("线程B");
  13.             });
  14.  
  15.             //开辟子线程,并行执行
  16.             Parallel.For(03(a) =>
  17.             {
  18.                 System.Threading.Thread.Sleep(1500);
  19.                 MessageBox.Show("线程" + a);
  20.             });
  21.  
  22.             MessageBox.Show("线程执行完毕");
  23.         }


4:自己另开线程,监控线程状态(不会阻塞主线程)

其实我们可以单独另外开辟一个新的线程了,用来监控所有子线程的运行状态,当这些被监控的线程都执行完毕后即可执行自己的逻辑,当然也可以封装一个回调函数用于方便调用。

线程的状态有很多:例如Created(已经创建),Running(运行中),RanToCompletion(运行完毕)等

我们就可以利用这些线程状态来做一些自定义操作。例如这里的等待线程执行完毕等。

  1. static void Main(string[] args)
  2.         {
  3.             Task task = new Task(() =>
  4.             {
  5.                 System.Threading.Thread.Sleep(2000);
  6.                 Console.WriteLine("线程执行完了");
  7.             });
  8.             task.Start();
  9.  
  10.  
  11.             Console.WriteLine("-------开始监听单线程执行-------");
  12.             //一个单独的线程 监控其他线程的状态
  13.             Task monitor_task = new Task(() =>
  14.             {
  15.                 while (true)
  16.                 {
  17.                     if (task.Status == TaskStatus.RanToCompletion) 
  18.                     {
  19.                         Console.WriteLine("监控到线程执行完了");
  20.                         break;
  21.                     }
  22.                 }
  23.             });
  24.             monitor_task.Start();
  25.  
  26.             Console.ReadLine();
  27.         }


监控多个线程其实也是一样,监控的时候判断执行完成线程的个数就行了

  1.   static void Main(string[] args)
  2.         {
  3.             Task[] tasklist = new Task[10];
  4.  
  5.             Dictionary<objectobject> dic = new Dictionary<objectobject>();
  6.             for (int i = 0; i < 10; i++)
  7.             {
  8.                 Task task = new Task((a) =>
  9.                 {
  10.                     System.Threading.Thread.Sleep(1000);
  11.                     dic.Add(a, a);
  12.                 }, i);
  13.  
  14.                 tasklist[i] = task;
  15.                 task.Start();
  16.             }
  17.  
  18.             //用户监控其他线程的状态
  19.             Task monitor_task = new Task(() =>
  20.             {
  21.                 //监控线程的个数
  22.                 int i = 0;
  23.                 while (true)
  24.                 {
  25.                     i = 0;
  26.                     foreach (Task item in tasklist)
  27.                     {
  28.                         if (item.Status == TaskStatus.RanToCompletion)
  29.                         {
  30.                             i++;
  31.                         }
  32.                     }
  33.                     if (i == 10)
  34.                     {
  35.                         Console.WriteLine("所有线程执行完毕" + dic.Count);
  36.                         break;
  37.                     }
  38.                 }
  39.             });
  40.             monitor_task.Start();
  41.             Console.ReadLine();      
  42.         }



欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739。有需要软件开发,或者学习软件技术的朋友可以和我联系~(Q:815170684)

评价

饰心

2019/4/23 10:12:34

学到了

剑轩:@饰心似乎达成了共识

2019/7/19 21:07:24 回复

是伍尚金哇_v

2019/12/4 12:03:02

不是Task不用task.start()了吗[吃惊]

css弹性盒子flex布局

css弹性盒子由于版本不同浏览器问题造成了一些不同的写法display:flexbox;在google浏览器中如果使用下面的写法就不行displa...

可输入下拉文本框据输入动态加载数据 jquery-editable-select

用到一个jquery-editable-select的控件github地址:https://github.com/indrimuska/jquery-editable-select这个插件的原理是...

.net mvc分部页.net core分部页

.net分部页的三种方式第一种:@Html.Partial(&quot;_分部页&quot;)第二种:@{ Html.RenderPartial(&quot;分部页&quot;);}...

css中单位pxemrem和vh/vw的理解

&gt;px像素(Pixel)。相对长度单位。像素px是相对于显示器屏幕分辨率而言的。em是相对长度单位。相对于当前对象内文本的字...

让IIS支持webp格式图片让IIS支持vtt格式iis设置mime类型iis配置支持的类型

webp格式图片可以让图片体积变小。也让下载图片变得更加困难一点 在线制作webp工具 https://www.upyun.com/webp?utm_mediu...

网页上传文件断点续传的实现无视文件大小上传以及datatables基本用法

首先明白js是客户带执行代码,c#是服务器上执行代码。本地文件需要用到js处理,服务器端接受c#代码处理1.HTML页面,文件信...

如何使用图标像使用文字一样使用文本图标的方法

1.首先在Iconfont-阿里巴巴矢量图标库上面找到你需要的图标然后加入你的购物车然后选择图标;注意:每个类型的图标会大小不...

使用七牛云的cdn服务提高图片的加载速度

CDN介绍CDN的全称是Content Delivery Network,即内容分发网络。CDN加速主要是加速静态资源,如网站上面上传的图片、媒体,...

通俗易懂什么是.NET?什么是.NET Framework?什么是.NET Core?

朋友圈@蓝羽 看到一篇文章写的太详细太通俗了,搬过来细细看完,保证你对.NET有个新的认识理解原文地址:https://www.cnblo...

JS监听input、keydown有输入法时打字完成后触发事件

在给输入框绑定input或keydown事件时预期效果是有输入法时,输入中文后触发事件,不希望输一个字母就触发一次事件可以用到c...

修改了css后让浏览器从缓存中更新

当我们修改了css后,如果不做一些操作,浏览器是不会自动更新我们的样式文件的。除非是过期或者用户手动刷新清理缓存等。所...

C MVC RedirectToAction跳转时候传递参数Action之间传值

MVC Action之间传值,页面跳转传值方法一:路由传值很简单直接使用 RedirectToAction(string actionName, string controller...

当你工作遇到以下几种状态时请果断跳槽走人

已经有想跳槽的念头,但是一直磨磨蹭蹭、犹犹豫豫的混日子,这种念头或者一直持续,或者是不是冒出头来占据你的思维了。于...

C与Java二进制编码转换补码

在C#与Java接口对接时,需要将图片转化为二进制编码传输,这时候发现c#转换出来的编码的值是0~255的范围,而java方需要的是...