lock语句和线程安全

来源:互联网 发布:淘货源软件下载 编辑:程序博客网 时间:2024/05/17 09:23

--------------以下部分内容摘自《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);        }

多次运行程序,并不能得到正确的值。这是因为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语句。



1 0
原创粉丝点击