--------------以下部分内容摘自《C#高级编程(第八版)》---------------
先来看一个简单的Demo:
首先是两个类的定义
Student类,只有一个简单的Num属性
public class Student { public int Num { get; set; } }Handler类
public class Handler { private Student student; public Handler(Student student) { this.student = student; } public void IncreaseNum() { for (int i = 0; i < 10000; i++) { student.Num++; } } }Main方法中创建一个Student类作为Task对象的构造函数参数。tasks是一个Task数组,程序中启动所有任务后,等待所有任务的执行完成。最后将状态值写入控制台。理论值应该是50*10000=500000.
static void Main(string[] args) { int taskNum = 50; var student = new Student(); var tasks = new Task[taskNum]; for (int i = 0; i < taskNum; i++) { tasks[i] = Task.Run(()=>new Handler(student).IncreaseNum()); } for (int i = 0; i < taskNum; i++) { tasks[i].Wait(); } Console.WriteLine("Num:{0}",student.Num); }
要解决这个问题,必须在这个程序中添加同步的功能,可以使用lock语句。
lock语句是设置锁定和解除锁定的一种简单方式。进行了锁定后,一次只有一个线程能访问相同的实例。在lock语句块的最后,对象的锁定被解除,另一个等待锁定的线程就可以获得该锁定块了。需要注意的是,lock锁定的成员只能是引用类型,否则编译不能通过。
lock语句的使用方法:
lock(obj) { //do something }
在Handler类的IncreaseNum方法中添加lock语句
public void IncreaseNum() { for (int i = 0; i < 10000; i++) { lock (student) { student.Num++; } } }运行程序,就可以得到正确的结果500000。
然而,这样的程序还存在一个问题:程序中所有使用Num变量的地方都必须添加lock语句块。
所以,如果要确保程序中访问Student的Num变量都是线程安全的,就必须修改Student的实现。
public class Student { private int num = 0; private object syncRoot = new object(); public int Num { get { return num; } } public int IncreaseNum() { lock (syncRoot) { return ++num; } } }同时Handler类的IncreaseNum方法也做如下改变
public void IncreaseNum() { for (int i = 0; i < 10000; i++) { this.student.IncreaseNum(); } }运行程序,同样可以得到正确的结果。
Student的更改采用了SyncRoot模式,通过SyncRoot模式,创建一个私有对象syncRoot,将这个对象用于lock语句。