线程基础
- 如何编写高性能高响应的应用程序
- 多线程并行编程的详尽指导
- 低耗电量以及多个计算核心变得比提高并行计算更为重要
- 如何有效的使用多个CPU核心来最优化性能,并同时通过在特定时间只运行需要的程序来节省电池电量
- 线程会消耗大量的操作系统资源,多线程共享一个物理处理器将导致操作系统忙于管理线程而无法运行程序
- 需要使用System.Threading这个类
- 创建线程
- 正在执行的程序实例可被称为一个进程,进程由一个或多个线程组成
- Thread t= new Thread(PrintNumbers);
- t.strat();
- 以上代码就是构造一个线程,并指定运行线程的方法名为:PrintNumbers;
- 暂停线程
- 在新线程指定的方法中,引用了Thread.Sleep(TimeSpan.FromSeconds(2))方法;
- 线程休眠一段时间它会占用尽可能少的CPU时间.
- 线程等待
- 在主线程中调用t.Join()方法,该方法允许我们等待直到线程t结束
- 当线程t完成时,主程序会继续运行
- 借助该技术可以实现两个线程间同步执行步骤
- 终止线程
- 主线程调用t.Abort();方法
- 给线程注入ThreadAbortException方法,导致线程被终结
- 非常危险,异常可以在任何时刻发生并可能摧毁应用程序,
- 另外,该技术也不一定总能终止线程,目标线程可以通过处理异常并调用Thread.ResetAbort方法来拒绝被终止
- 并不推荐使用此方法,可优先使用其他方法,比如提供一个CancellationToken方法来取消线程的执行
- 检测线程状态
- 调用t.ThreadState.ToString()方法,检测线程当前的状态
- ThreadState属性是C#枚举对象,对象类型有Unstarted、Running、WaitSleepJoin等值
- ThreadState.CurrentThread静态属性获得当前Thread对象
- 线程优先级
- 线程优先级决定了线程可占用多少CPU时间
- ThreadPriority.Highest(Lowest)
- 前台线程和后台线程
- 默认情况下,显式创建的线程是前台线程,通过设置线程对象的IsBackground属性为Ture来创建一个后台线程.
- 进程会等待所有的前台线程完成后再结束工作,但是如果只剩下后台线程,则会直接结束工作
- 如果程序定义了一个不会完成的前台程序,主程序并不会正常结束
- 向线程传递参数
-
var sample = new ThreadSample(10);
var threadone =new Thread(sample.CountNumbers);
var threadTwo = new Thread (Count);
var threadThree =new Thread( () => CountNumbers(12) );
1.首先创建了ThreadSample类的一个对象,并提供了一个迭代次数,并使用该对象的CountNumbers方法启动线程,参数传入方式:通过ThreadSample对象的构造函数传入的,传入参数的方式是使用相同的间接方式将参数传入
2.使用Thread.Start(8)传入参数,要求:在线程中启动的方法必须接受object类型的单个参数,然后强制类型转换为相应的参数形式(int)
3.lambda表达式传参,lambd表达式定义了一个不属于任何类的方法,我们创建了一个方法,该方法使用需要的参数调用了另一个方法,并在另外一个线程中运行该方法
使用lambda表达式引用另一个C#对象的方式被称为闭包,当在lambda表达式中使用局部变量时,C#会自动生成一个类,并将该变量作为该类的一个属性,所以实际上该方式与第一种传参的方式一样,但是我们不需要定义该类,C#编译器会自动帮助我们实现
问题:如果在多个lambda中使用相同的变量,他们会共享该变量值。
-
- 使用C#中的lock关键字
- 确保当一个线程使用某些资源时,同时其他线程无法使用该资源
- 必要性以及线程安全
- 竞争条件:两个线程同时操作一个对象,导致对象状态紊乱
- 竞争条件是多线程环境中非常常见的导致错误的原因
- 为了避免竞争条件,必须保证当有线程操作对象时,所有其他线程必须等待直到当前线程完成操作
- 使用lock关键字来实现这种行为:lock(_syncRoot){Count++;}
- 如果锁定了一个对象,需要访问该对象的所有其他线程则会处于阻塞状态,并等待直到该对象解除锁定
- 使用Monitor
- deadlock:(死锁)另外一个常见的多线程错误,死锁将导致程序停止工作
- 使用新的Monitor类来避免死锁;之前描述过的lock关键字则是用于创建死锁
- 直接使用Monitor类,其拥有TryEnter方法,接受一个超时参数,如果在我们能够获取被lock保护的资源之前,超时参数过期,则该方法会返回flase.
- 处理异常
- 程序启动时的异常处理,有两种:第一、在线程开始的时候启动异常,第二、在线程中启动捕获异常。
- 如果直接使用线程,一般来说不要在线程中抛出异常,而是在线程代码中使用try/catch代码块。