前言
最近工作不是很忙,想把买了很久了的《C#多线程编程实战》看完,所以索性把每一章的重点记录一下,方便以后回忆。
第1章 线程基础
1.创建一个线程
using System; using System.Threading; namespace Chapter1.Recipe1 { class Program { static void Main(string[] args) { Thread t = new Thread(PrintNumbers); t.Start(); PrintNumbers(); } static void PrintNumbers() { Console.WriteLine("Starting..."); for (int i = 1; i < 10; i++) { Console.WriteLine(i); } } } }
2.改变线程状态的方法
①线程暂停-Sleep()方法
它是Thread类中的一个静态方法,使用方式是Thread.Sleep(timeout);
②线程等待-Join()方法
它是线程实例的一个实例方法,使用时t.Join();(t是线程的一个实例)
说明:该方法允许我们等待直到线程t完成,当t执行完成后,主线程会继续执行。借助这个方法可以简单实现两个线程之间的同步执行顺序。第一个线程会等第二个线程执行完成后再继续执行。此时第一个线程处于阻塞状态。
③终止线程-Abort()方法
它也是一个实例方法,使用方法是t.Abort()。
说明:它终止线程的方法是给线程注入一个ThreadAbortException,导致线程被终结。这个异常可能会导致程序的彻底崩溃。并且这个异常并不一定会终止线程,目标线程可以通过处理该异常并调用Thread.ResetAbort方法来拒绝终止线程。终止线程的方法最好是通过添加一个标志来进行线程的终止。
④检查线程的状态-ThreadState
ThreadState是一个枚举类型,包含了线程运行的几种状态。
⑤线程的优先级
ThreadPriority也是一个枚举类型
public enum ThreadPriority { Lowest = 0, BelowNormal = 1, Normal = 2, AboveNormal = 3, Highest = 4, }
使用方法:
threadOne.Priority = ThreadPriority.Highest;
⑥前台线程和后台线程
IsBackground属性用来区别前台线程和后台线程,默认情况下创建的线程都是前台线程,当把线程的属性IsBackground设置为true的时候,那么则创建一个后台线程。
前台线程和后台线程的区别:进程会等待所有的前台线程执行完之后再去结束,如果只剩下了后台线程则进程会直接结束。
⑦向线程传递参数
方式一:
定义一个类,在类的构造函数当中进行参数的赋值
1 class ThreadSample 2 { 3 private readonly int _iterations; 4 5 public ThreadSample(int iterations) 6 { 7 iterations = iterations; 8 } 9 public void CountNumbers() 10 { 11 for (int i = 1; i <= _iterations; i++) 12 { 13 Thread.Sleep(TimeSpan.FromSeconds(0.5)); 14 Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i); 15 } 16 } 17 }
调用:
1 var sample = new ThreadSample(10); 2 var threadOne = new Thread(sample.CountNumbers); 3 threadOne.Name = "ThreadOne"; 4 threadOne.Start(); 5 threadOne.Join();
方式二:采用ParameterizedThreadStart的方式
ParameterizedThreadStart是一个委托类型,接收一个object类型的参数。
public delegate void ParameterizedThreadStart(object obj);
使用方法:
Count函数有一个object类型的参数
1 static void Count(object iterations) 2 { 3 CountNumbers((int)iterations); 4 } 5 6 static void CountNumbers(int iterations) 7 { 8 for (int i = 1; i <= iterations; i++) 9 { 10 Thread.Sleep(TimeSpan.FromSeconds(0.5)); 11 Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i); 12 } 13 }
调用:
1 ParameterizedThreadStart paramThread = new ParameterizedThreadStart(Count); 2 Thread threadMy = new Thread(paramThread); 3 threadMy.Name = "My"; 4 threadMy.Start(10); 5 threadMy.Join();
方式三:采用lambda表达式
1 var threadThree = new Thread(() => CountNumbers(12)); 2 threadThree.Name = "ThreadThree"; 3 threadThree.Start(); 4 threadThree.Join();
() => CountNumbers(12) lambda表达式定义了一个不属于任何类的方法。我们创建了一个方法,该方法使用需要的参数调用了另一个方法,并在另一个线程中运行该方法。
⑧lock关键字
lock关键字来解决线程之间的竞争条件。
在C# lock关键字定义如下: lock(expression) statement_block,其中expression代表你希望跟踪的对象,通常是对象引用。
代码示例如下:
private static object ojb = new object();
lock(obj)
{
//锁定运行的代码段
}
假设线程A先执行,线程B稍微慢一点。线程A执行到lock语句,判断obj是否已申请了互斥锁,判断依据是逐个与已存在的锁进行object.ReferenceEquals比较(此处未加证实),如果不存在,则申请一个新的互斥锁,这时线程A进入lock里面了。
⑨Monitor类锁定资源
这个类用来避免死锁,之前的lock关键字用来创建死锁,其实lock是Monitor的一个语法糖
首先lock和Minitor有什么区别呢?
Monitor和Lock的区别
1.Lock是Monitor的语法糖。
2.Lock只能针对引用类型加锁。
3.Monitor能够对值类型进行加锁,实质上是Monitor.Enter(object)时对值类型装箱。
4.Monitor还有其他的一些功能。
貼り付け元 <http://www.cnblogs.com/chengxingliang/p/3150731.html>
其实lock在IL代码中会被翻译成Monitor。也就是Monitor.Enter(obj)和Monitor.Exit(obj).
lock(obj)
{
}
等价为:
try
{
Monitor.Enter(obj)
}
catch()
{}
finally
{
Monitor.Exit(obj)
}
所以lock能做的,Monitor肯定能做,Monitor能做的,lock不一定能做。那么Monitor额外的功能呢?
1:Monitor.TryEnter(obj,timespan)----timeout之后,就不执行这段代码了。lock可是一直会死等的。
2:还有Monitor.Wait()和Monitor.Pulse()。在lock代码里面如果调用了Monitor.Wait(),会放弃对资源的所有权,让别的线程lock进来。然后别的线程代码里Pulse一下(让原线程进入到等待队列),然后在Wait一下释放资源,这样原线程的就可以继续执行了(代码还堵塞在wait那句话呢)。
也就是说,必须两个或多个线程共同调用Wait和Pulse,把资源的所有权抛来抛去,才不会死锁。
Monitor的常用属性和方法:
Enter(Object) 在指定对象上获取排他锁。
Exit(Object) 释放指定对象上的排他锁。
IsEntered 确定当前线程是否保留指定对象锁。
Pulse 通知等待队列中的线程锁定对象状态的更改。
PulseAll 通知所有的等待线程对象状态的更改。
TryEnter(Object) 试图获取指定对象的排他锁。
TryEnter(Object, Boolean) 尝试获取指定对象上的排他锁,并自动设置一个值,指示是否得到了该锁。
Wait(Object) 释放对象上的锁并阻止当前线程,直到它重新获取该锁。