lock语句和线程安全

--------------以下部分内容摘自《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
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值