一.Parallel

和Task很像,启动多个线程计算,但是主线程也参与计算,所以它也会卡界面。它本质上相当于Task+WaitAll,只要用到了Task开启多个线程并且要WaitAll,就可以用Parallel,还可以节约一个线程(主线程参与运算)。

代码如下:

Parallel.Invoke(() => this.DoSomethingLong("btnParallel_Click_002"),() => this.DoSomethingLong("btnParallel_Click_001"),() => this.DoSomethingLong("btnParallel_Click_003"),() => this.DoSomethingLong("btnParallel_Click_004"),() => this.DoSomethingLong("btnParallel_Click_005"));

返回结果如下:

Parallel还有一些API,比如:

Parallel.For(0, 5, t =>
{this.DoSomethingLong($"btnParallel_Click_00{t}");
});

这段代码借助了for循环,和上面代码写的是一样的。还可以这样写:

Parallel.ForEach(new int[] { 0, 1, 2, 3, 4 }, t =>
{this.DoSomethingLong($"btnParallel_Click_00{t}");
});

这里有个问题,如果要循环100次请求,有100个线程给我们用吗?当然不会的,这时候要设定一个开启线程并发的最大值,开启的线程只有执行完了,下一个线程才可以执行,可以这样写:

ParallelOptions options = new ParallelOptions()
{MaxDegreeOfParallelism = 3   //最大3个线程并发任务
};Parallel.ForEach(new int[] { 0, 1, 2, 3, 4 }, options, t =>
{this.DoSomethingLong($"btnParallel_Click_00{t}");
});

执行结果如下:

如果在执行过程中还需要人工干预的话,可以加上state,并对state进行stop,break操作,比如下面:

Parallel.ForEach(new int[] { 0, 1, 2, 3, 4 }, options, (t, state) =>{this.DoSomethingLong($"btnParallel_Click_00{t}");//state.Stop();//结束全部的//state.Break();//停止当前的//return;});

二.多线程的异常处理

private void btnThreadCore_Click(object sender, EventArgs e){try{TaskFactory taskFactory = new TaskFactory();List<Task> taskList = new List<Task>();for (int i = 0; i < 20; i++){string name = string.Format($"btnThreadCore_Click_{i}");Action<object> act = t =>{//多线程内部要加try catch,处理自己的异常try{Thread.Sleep(2000);//遇上11,则会抛出异常,显示“执行失败”if (t.ToString().Equals("btnThreadCore_Click_11")){throw new Exception(string.Format($"{t} 执行失败"));}//遇上12,则会抛出异常,显示“执行失败”if (t.ToString().Equals("btnThreadCore_Click_12")){throw new Exception(string.Format($"{t} 执行失败"));}Console.WriteLine("{0} 执行成功", t);}catch (Exception ex){Console.WriteLine(ex.Message);}};taskList.Add(taskFactory.StartNew(act, name));}}catch (AggregateException aex){foreach (var item in aex.InnerExceptions){Console.WriteLine(item.Message);}}catch (Exception ex){Console.WriteLine(ex.Message);}}

显示结果:

好了,如果我们内部不抓取catch,放到外面去抓catch呢?代码这样的:

private void btnThreadCore_Click(object sender, EventArgs e){try{TaskFactory taskFactory = new TaskFactory();List<Task> taskList = new List<Task>();for (int i = 0; i < 20; i++){string name = string.Format($"btnThreadCore_Click_{i}");Action<object> act = t =>{Thread.Sleep(2000);if (t.ToString().Equals("btnThreadCore_Click_11")){throw new Exception(string.Format($"{t} 执行失败"));}if (t.ToString().Equals("btnThreadCore_Click_12")){throw new Exception(string.Format($"{t} 执行失败"));}Console.WriteLine("{0} 执行成功", t);};taskList.Add(taskFactory.StartNew(act, name));}}catch (AggregateException aex){foreach (var item in aex.InnerExceptions){Console.WriteLine(item.Message);}}catch (Exception ex){Console.WriteLine(ex.Message);}}

这样是很危险的,异常不抛出,外面获取不了,还以为全部执行成功了。

外面能不能获取到异常呢?可以的,代码如下:

private void btnThreadCore_Click(object sender, EventArgs e){try{TaskFactory taskFactory = new TaskFactory();List<Task> taskList = new List<Task>();for (int i = 0; i < 20; i++){string name = string.Format($"btnThreadCore_Click_{i}");Action<object> act = t =>{try{Thread.Sleep(2000);if (t.ToString().Equals("btnThreadCore_Click_11")){throw new Exception(string.Format($"{t} 执行失败"));}if (t.ToString().Equals("btnThreadCore_Click_12")){throw new Exception(string.Format($"{t} 执行失败"));}Console.WriteLine("{0} 执行成功", t);}catch (Exception ex){Console.WriteLine(ex.Message);}};taskList.Add(taskFactory.StartNew(act, name));}//就加这一句,意思是等待所有线程全部执行完Task.WaitAll(taskList.ToArray());}catch (AggregateException aex){foreach (var item in aex.InnerExceptions){Console.WriteLine(item.Message);}}catch (Exception ex){Console.WriteLine(ex.Message);}}

这样就可以在委托外面获取到异常了:

不过,还是不要这么做,最好用第一种方法,委托内部try catch,获取异常,因为不知道外面有没有

taskList.Add(taskFactory.StartNew(act, name));   这个语句。

三.取消线程

场景:多个线程并发,如果一个任务失败就要求其他任务不再执行了。

线程取消不是操作线程,而是操作信号量(共享变量,多个线程都能访问到的东西,变量/数据库的数据/硬盘数据)。
每个线程在执行的过程中,经常去查看下这个信号量,然后自己结束自己,线程不能别人终止,只能自己干掉自己,延迟是少不了的。

CancellationTokenSource可以在cancel后,取消没有启动的任务

看一下执行结果:

四.多线程的临时变量

再看一个,每个循环中定义一个K,并且等于i

五.线程安全

循环10000次,TotalCount每次加1,IntList集合每次新增一个元素,那么按照原计划,打印出来,TotalCount=10000,IntLis.Count = 10000。运行后看结果:

不是预想的10000。因为是多线程,可能变量被几个线程同时在修改,所以就少了,变量应该放在多线程内部,不要共享出去,这样才是安全的。

解决多线程冲突第一个办法:lock,lock的方法块儿里面是单线程的;lock里面的代码要尽量的少
先看lock:

lock是语法糖, lock==Monitor.Enter,检查下这个变量有没有被lock   有就等着,没有就占有,然后进去执行,执行完了释放。

看一下怎样用lock改造上面的代码:

先定义一个让lock锁定的变量,是个静态的

结果:

这样就不会少了,lock对btnThreadCore_Click_Lock这个变量(变量必须是引用类型)进行锁定,其他线程如果进来,发现btnThreadCore_Click_Lock被锁定了,只有等待释放,所以 lock块中每次只允许进入一个线程,lock块变成了单线程,既然lock块变成了单线程,那么里面的代码越少越好,早点执行完。

有两个错误的使用lock的方法:

1.lock(this)

锁定了当前实例,别的地方如果要使用这个变量呢?都被锁定了 ,如果每个实例想要单独的锁定 ,用 private object

2.锁定一个字符串

比如 string a="123456"; lock(a)

a="123456",已经被内存中分配空间了,如果下次定义了b也等于"123456"(享元模式的内存分配,字符串是唯一的),string是引用类型,系统会把a和b当作同一个内存地址,而a已经被锁定,相当于b也被锁定了,如果此时有线程想锁定b,就只能等待。

解决多线程冲突第二个办法:没有冲突,从数据上隔离开,即线程之间不要有共享数据,每个线程之间不要数据交叉,各自处理自己的数据,这样就避免了冲突。

13异步多线程(三)Parallel,线程安全相关推荐

  1. java多线程三之线程协作与通信实例

    多线程的难点主要就是多线程通信协作这一块了,前面笔记二中提到了常见的同步方法,这里主要是进行实例学习了,今天总结了一下3个实例: 1.银行存款与提款多线程实现,使用Lock锁和条件Condition. ...

  2. VC++中多线程学习(MFC多线程)三(线程同步包含:原子互锁、关键代码段、互斥器Mutex、Semaphores(信号量)、Event Objects(事件))

    目录 ​​​​​​​​​​​​ 线程同步的必要性: 2.解决同步问题的方法 2.1原子互锁家族函数 2.2Critical Sections(关键代码段.关键区域.临界区域) 2.3 互斥器Mutex ...

  3. Java讲课笔记33:多线程概述与线程创建

    文章目录 零.本讲学习目标 1.了解多线程的概念 2.掌握多线程创建的三种方式 3.熟悉创建多线程三种方式的主要区别 一.进程概述 (一)进程定义 (二)三维度看待进程模型 (三)进程说明 (四)进程 ...

  4. java 多线程输出_[Java多线程]ABC三个线程顺序输出的问题

    大概的问题是这样的: 有A,B,C三个线程, A线程输出A, B线程输出B, C线程输出C 要求, 同时启动三个线程, 按顺序输出ABC, 循环10次 这是一个多线程协同的问题, 本身多线程是没有执行 ...

  5. java异步多线程 判断线程状态_java多线程和异步回调

    在实际开发过程中遇到的多线程情况不多,但是在生产环境中多线程是最基本的情况,java面试时也会考到,所以看看多线程的知识还是很有必要的. Thread,Runnable,Callable,Future ...

  6. 多任务场景下单线程异步多线程多进程

    多任务的场景:1.爬取不同url的内容,爬取同一个url分页内容.比如:豆瓣图书 Top 250 https://book.douban.com/top250?start=0 实现豆瓣图书Top250 ...

  7. 深入理解Java虚拟机(第三版)-13.Java内存模型与线程

    13.Java内存模型与线程 1.Java内存模型 Java 内存模型的主要目的是定义程序中各种变量的访问规则,即关注在虚拟机中把变量值存储到主内存和从内存中取出变量值的底层细节 该变量指的是 实例字 ...

  8. Task/Parallel实现异步多线程

    代码: #region Task 异步多线程,Task是基于ThreadPool实现的{//TestClass testClass = new TestClass();//Action<obje ...

  9. .NET异步和多线程系列(四)- 多线程异常处理、线程取消、多线程的临时变量问题、线程安全和锁lock

    本文是.NET异步和多线程系列第四章,主要介绍的是多线程异常处理.线程取消.多线程的临时变量问题.线程安全和锁lock等. 一.多线程异常处理 多线程里面抛出的异常,会终结当前线程,但是不会影响别的线 ...

  10. Java 多线程(三) 线程的生命周期及优先级

    Java 多线程(三) 线程的生命周期及优先级 线程的生命周期 线程的生命周期:一个线程从创建到消亡的过程. 如下图,表示线程生命周期中的各个状态: 线程的生命周期可以分为四个状态: 1.创建状态: ...

最新文章

  1. NAR:antiSMASH数据库2—次级代谢物基因簇预测
  2. strlen与sizeof区别
  3. 哈夫曼编码 译码java_基于Java的哈夫曼编码译码系统_报告毕业论文
  4. PlentyOfFish.com .NET网站的又一传奇
  5. zabbix使用脚本监控
  6. plex实现流媒体服务器_如何从Plex Media Server离线查看下载和同步媒体
  7. 橡皮擦_日本推出改邪归正橡皮擦,看得我头顶一凉
  8. (转)javascript匿名函数
  9. linux my.cnf基本参数,Linux中MySQL配置文件my.cnf参数说明
  10. USACO1.1.2 - Greedy Gift Givers
  11. 为什么数据可视化很重要
  12. colormap保存 matlab_matlab中自定义colormap的保存与调用
  13. TeeChart安装教程
  14. Chrome鼠标手势插件:CrxMouse
  15. 通过PS修出自然的大长腿
  16. BCH闹剧,又一场带血的分叉
  17. Tableau的用法
  18. 学python可以改善思维_基于培养思维能力的Python语言程序设计教学
  19. 蓝牙耳机什么牌子好_盘点千元内最好的蓝牙耳机
  20. c++ 0x8000ffff灾难性故障_硬盘出了故障就换?教你一招,不花一分钱就能修复!...

热门文章

  1. 怪诞行为学(Predictably Irrational)
  2. 修改封装系统的默认壁纸、锁屏和OEM信息
  3. MySql生日闰月处理
  4. 使用Whisper框架快速为项目添加国际化支持
  5. KVM/QEMU虚拟机申请和释放内存的步骤
  6. phalapi-入门篇6(小技巧和浅谈API适用范围以及入门篇总结)
  7. 计算机鼠标左键不起作用,电脑鼠标左键突然不好用了 右键没问题
  8. 低级程序员和高级程序员的区别,难道这就是最强王者的世界吗?
  9. 卫星勘测洪水数据网站
  10. c51 c语言 16位二进制转换为bcd码,16位二进制转换为BCD码的C51汇编程序.doc