20181106_线程之异常_取消_变量_安全Lock

一. 线程的异常处理:

 try
            {
                TaskFactory taskFactory = new TaskFactory();
                List<Task> taskList = new List<Task>();

                //线程里面的异常是被吞掉了,因为已经脱离try catch的范围了  如果真想让线程之外的Catch抓到异常, 那么只有使用 WaitAll 抓到多线程里面全部的异常
                //实际开发: 线程里面的action不允许出现异常,自己使用try catch处理好
                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($"Exception:{ex.Message}"); //
                        }
                    };
                    taskList.Add(taskFactory.StartNew(act, name));
                }
               // Task.WaitAll(taskList.ToArray());使用 WaitAll 捕获多线程里面全部的异常; 但是也不能总是WaitAll, 并且很多业务也不能使用WaitAll来处理

            }
            catch (AggregateException aex) //专门处理多线程的异常, 里面的异常有多项
            {
                foreach (var item in aex.InnerExceptions)
                {
                    Console.WriteLine(item.Message);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

 

二. 线程的取消,

a)   场景: 多个线程并发,某个失败后,希望通知别的线程,都停下来, 尽量使用CancellationTokenSource, 不要自己创建bool变量

 try
            {
                TaskFactory taskFactory = new TaskFactory();
                List<Task> taskList = new List<Task>();

                 //多个线程并发,某个失败后,希望通知别的线程,都停下来
                 //task是外部无法中止(中间停止),Thread.Abort不靠谱,因为线程是OS的资源,无法掌控啥时候取消
                 //线程的停止: 线程自己停止自己--公共的访问变量--修改它---线程不断的检测它(延迟少不了)
                 //CancellationTokenSource去标志任务是否取消  Cancel取消   IsCancellationRequested  是否已经取消了
                 //Token 启动Task的时候传入,那么如果Cancel了,这个任务会放弃启动,抛出一个异常
                 // 当一个线程不执行, 后面的线程全部都不能再执行了
                 CancellationTokenSource cts = new CancellationTokenSource();//bool值 //bool flag = true; 线程安全的
                for (int i = 0; i < 40; i++)
                {
                    string name = string.Format("btnThreadCore_Click{0}", i);
                    Action<object> act = t =>
                    {
                        try
                        {
                            //if (cts.IsCancellationRequested)
                            //{
                            //    Console.WriteLine("{0} 取消一个任务的执行", t);
                            //}
                            Thread.Sleep(2000);
                            if (t.ToString().Equals("btnThreadCore_Click11"))
                            {
                                throw new Exception(string.Format("{0} 执行失败", t));
                            }
                            if (t.ToString().Equals("btnThreadCore_Click12"))
                            {
                                throw new Exception(string.Format("{0} 执行失败", t));
                            }
                            if (cts.IsCancellationRequested)//启动40个线程, 每一个线程都检查这个信号量, 是否改任务已经被取消
                            {
                                Console.WriteLine("{0} 放弃执行", t);
                                return;
                            }
                            else
                            {
                                Console.WriteLine("{0} 执行成功", t);
                            }
                        }
                        catch (Exception ex)
                        {
                            cts.Cancel(); //当执行抛出异常后, (t.ToString().Equals("btnThreadCore_Click11")); 将信号量设置为false, 表示取消任务
                            Console.WriteLine(ex.Message);
                        }
                    };
                    taskList.Add(taskFactory.StartNew(act, name, cts.Token));
                    //如果一个线程在启动的时候标识了cts.Token, 当cts.Cancel()时, 如果这个线程还没有启动, 则会被标识为取消一个任务执行; 如果这个线程已经启动, 但是还没有执行, 那么会被放弃执行
                }
                Task.WaitAll(taskList.ToArray());

            }
            catch (AggregateException aex) //专门处理多线程的异常
            {
                foreach (var item in aex.InnerExceptions)
                {
                    Console.WriteLine(item.Message);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

三. 多线程临时变量

for (int i = 0; i < 5; i++)
                {
                    Task.Run(() =>
                      {
                          Thread.Sleep(100);
                          Console.WriteLine(i);  //打印结果 5个5
                      });
                }

                for (int i = 0; i < 5; i++)
                {
                    int k = i;
                    Task.Run(() =>
                    {
                        Thread.Sleep(100);
                        Console.WriteLine(k); //打印结果 4,3,1,2,0
                    });
                }

                //i最后是5      全程就只有一个i  等着打印的时候,i==5
                //k             全程有5个k   分别是0 1 2 3 4
                //k在外面声明   全程就只有一个k    等着打印的时候,k==4
                int k = 0;
                for (int i = 0; i < 5; i++)
                {
                    //    int k = i;
                    k = i;
                    new Action(() =>
                    {
                        Thread.Sleep(100);
                        Console.WriteLine($"k={k} i={i}");
                    }).BeginInvoke(null, null);
                }

 

四. 线程安全:

try
            {
                TaskFactory taskFactory = new TaskFactory();
                List<Task> taskList = new List<Task>();

                //如何判断何时加锁: 线程内部声明的变量, 由于不共享的是线程安全的; 但是在线程外部操作的资源由于共享, 则就会造成线程不安全. 比如全局变量/数据库的某个值/磁盘文件
                int TotalCountIn = 0;//TotalCountIn属于线程外部变量, 会有线程安全有问题
                for (int i = 0; i < 10000; i++)//i属于线程外部变量, 会有线程安全有问题
                {
                    int newI = i;//newI 属于线程内部变量, 不会有线程安全有问题
                    taskList.Add(taskFactory.StartNew(() =>
                    {
                       //值类型不能lock
                        lock (btnThreadCore_Click_Lock)//lock后的方法块,任意时刻只有一个线程可以进入
                        //只能锁引用类型,占用这个引用链接   不要用string 因为享元  , 可能导致锁的是同一块内存区域
                        {   //这里就是单线程

                            this.TotalCount += 1;
                            TotalCountIn += 1;
                            this.IntList.Add(newI);
                        }

                    }));
                }
                Task.WaitAll(taskList.ToArray());
                Console.WriteLine(this.TotalCount);
                Console.WriteLine(TotalCountIn);
                Console.WriteLine(this.IntList.Count());
                #endregion
            }
            catch (AggregateException aex) //专门处理多线程的异常
            {
                foreach (var item in aex.InnerExceptions)
                {
                    Console.WriteLine(item.Message);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

五.   关于Lock变量的写法解释:

//为什么锁的变量要这样写private static readonly object btnThreadCore_Click_Lock = new object();
//private  防止外面也去锁
//static 全场唯一
//readonly不准修改, 如果不是readonly有可能会在锁的代码里面修改它
//object表示引用, 因为lock不能锁值类型

 

 六. 为什么不推荐锁this

 

//lock (this)
//{
//    //this form1的实例  每次实例化是不同的锁,同一个实例是相同的锁
//    //但是这个实例别人也能访问到,别人也能锁定
//    //最好不要锁this
//}

 

七. Lock到底是个什么? 

//Monitor.Enter(btnThreadCore_Click_Lock);
//lock 就是一个语法糖, 类似于monitor的写法, 也就是说可以将lock{ }块中的语句, 放到Monitor的Enter和exit中间
//Monitor.Exit(btnThreadCore_Click_Lock);
Monitor.Enter(btnThreadCore_Click_Lock);
//lock 就是一个语法糖, 类似于monitor的写法, 也就是说可以将lock{ }块中的语句, 放到Monitor的Enter和exit中间
{   //这里就是单线程

     this.TotalCount += 1;
     TotalCountIn += 1;
     this.IntList.Add(newI);
}
Monitor.Exit(btnThreadCore_Click_Lock);

八. 不使用lock解决,线程安全问题:

//使用lock 解决的时候,因为只有一个线程可以操作数据, 没有并发, 所以解决了问题    但是牺牲了性能,所以要尽量缩小lock的范围

//解决办法:1.  开发中最好不要有冲突, 能拆分就优先拆分, 比如在1亿条数据中进行操作, 那么可以先将数据拆分成不同的小块, 然后再合并成1亿条数据

//2. 不使用locak, 可以使用安全队列 System.Collections.Concurrent.ConcurrentQueue来解决,   一个线程去完成操作

  

原文地址:https://www.cnblogs.com/wxylog/p/9918074.html

时间: 2024-10-30 02:48:11

20181106_线程之异常_取消_变量_安全Lock的相关文章

Bash 中的 _ 是不是环境变量

首先,我们想到的会是 export(等价于 declare -x)命令: $ export | grep 'declare -x _=' 没有找到,那么结论就是 _ 不是环境变量?当然没那么简单,否则本篇文章就该结束了.别忘了还有 env(或者 printenv)命令: $ env | grep '_=' _=/usr/bin/env 这下怎么办,_ 到底是不是环境变量?谁说的对?然而下面还有更诡异的: $ bash -c "export | grep 'declare -x _='"

day02_变量_基本数据类型_数据类型的转换_Scanner_20150731

回顾: 昨天主要讲了3个方面的内容: 1.Linux 操作系统.开源的.免费 服务器端 目录.挂载点.安全级别高 /:根目录 /home/soft01:主目录/家 2.java开发环境 1).java源文件,经过编译,生成.class字节码文件 JVM加载.class并运行.class 跨平台.一次编程到处使用 2)JVM:java虚拟机 加载并运行.class JRE:java运行环境 除了包含JVM以外还包含运行java程序所需要的必须的环境 JRE=JVM+java系统类库 JDK:jav

C++旧源码向新标准移植陷井(一)_局部栈变量的生命周期

之前在VC++6.0上面写了下面这样的代码: int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int nRetCode = 0; //不重要的部分略过if (argc>1) {if (strcmp(argv[1],"createpcsstep") == 0) { CDlgCreateStepPcs dlg; dlg.DoModal(); break; } else if (strcmp(argv[1],"clea

IOS_地图_定位_天气预报_Block回调_单例

H:/1021/00_block回调.h /* 通过block回调 定义block代码块,目的是解析完成之后调用 返回值是 void 参数是 数组,里面的每个成员是一个NSString*/ typedef void(^WeatherFinishedBlock)(NSArray *dataList); @interface WeatherXMLPaser : NSObject // 解析器解析数据,参数1是要解析的数据,参数2是解析完毕回调的代码块 - (void)parserWeatherDat

C++ Primer笔记12_运算符重载_递增递减运算符_成员访问运算符

1.递增递减运算符 C++语言并不要求递增递减运算符必须是类的成员.但是因为他们改变的正好是所操作对象的状态,所以建议设定为成员函数. 对于递增与递减运算符来说,有前置与后置两个版本,因此,我们应该为类定义两个版本的递增与递减运算符. 问题来了,程序是如何区分前置和后置呢?因为都是++和-- 为了解决这个问题,后置版本的递增递减运算符接受一个额外的(不被使用)int类型的形参.当我们使用后置运算符时,编译器为这个形参提供一个值为0的实参.这个形参唯一的作用就是区分前置和后置运算符函数. 因为不会

软件工程_东师站_第十周作业

一.PSP Data Type Job start Int End Total 20160510 助教 团队作业二 20:00 5 21:00 55 20160511 助教 团队作业二.三 18:45 16 20:45 104 20160512 耐撕 站立会议 18:15   18:35 20 二.进度条   代码行数 博客字数 知识点 第一周 400 430 见我博客软件工程——师大站1 第二周 0 5200 见我博客软件工程_东师站_课堂笔记 第三周 0 63 站立会议.单元测试 第四周 1

Payssion,海外本地支付_海外本地收款_小语种本地支付_外贸收款_外贸网店收款_欧洲本地支付_俄罗斯本地支付_巴西支付_跨境支付_PAYSSION,让跨境支付更轻松!

Payssion,海外本地支付_海外本地收款_小语种本地支付_外贸收款_外贸网店收款_欧洲本地支付_俄罗斯本地支付_巴西支付_跨境支付_PAYSSION,让跨境支付更轻松! 首页 / 关于我们 / 关于Payssion 关于Payssion Payssion,让跨境支付更轻松! Payssion,专注于为中小企业和个人提供"安全.轻松.便捷"的全球在线支付解决方案,为客户提供全球在线收款服务.我们的团队成员来自支付宝.Paypal等支付公司,我们对全球支付方面有着长期的经验积累! 为什

android内部培训视频_第三节(3)_常用控件(ViewPager、日期时间相关、ListView)

第三节(2):常用控件之ViewPager.日期时间相关.ListView  一.ViewPager 实例:结合PagerAdapter滑动切换图片  二.日期时间相关:AnalogClock\DigitalClock\DatePicker\TimerPicker\DatePickerDialog\TimePickerDialog 三.ListView 实例1:城市选择器 实例2:自定义列表项 百度网盘视频下载地址:http://pan.baidu.com/s/1c0ip6la android内

相克军_Oracle体系_随堂笔记014-锁 latch,lock

1.Oracle锁类型     锁的作用     latch锁:chain,链     LOCK锁         排他锁(X)         共享锁(S) 2.行级锁:DML语句     事务锁TX         锁的结构         事务锁的加锁和解锁过程     只有排他锁         不影响读(CR块) 3.表级锁:TM     行级排他锁(Row exclusive)RX锁         当我们进行DML时,会自动在被更新的表上添加RX锁,可以执行LOCK命令显式的在表上