浅谈对C#-lock()的理解

来源:互联网 发布:烟台淘宝运营 编辑:程序博客网 时间:2024/05/19 07:25
C#中的lock语句是怎么回事,有什么作用?
C#中的lock语句将lock中的语句块视为临界区,让多线程访问临界区代码时,必须顺序访问。他的作用是在多线程环境下,确保临界区中的对象只被一个线程操作,防止出现对象被多次改变情况。
注意的地方有:lock对象必须是一个不可变对象,否则无法阻止另一个线程进入临界区。最好是privatestatic readonly 或者privatestatic。常见的lock(this)、lock(typeof (MyType)) 和 lock ("myLock") 都是不合理的写法。
原因:
1.lock(this),主要问题是如果类为实例化类,那么this也只是当前实例,不能起到锁定的作用,而且即使是静态类可以被锁定,那么由于尺寸问题,不如锁定不可变小对象合算。
2.lock(typeof())问题在于锁定对象类型当相于锁定类型中的静态对象部分,锁定了所有实例,速度慢;另一方面,有可能程序其他部分在访问该对象,已经替您锁定,将导致您锁定语句的挂起。原文(Here'swhy: Since there's one type object for all instances of a class, it wouldappear that locking it would provide a lock equivalent to locking a staticobject contained in your class. You would lock all instances of the class, waituntil other threads were done accessing any part of any instance, then lockaccess so you could access static members safely and without another threadinterfering.)

3.lock("")问题在于微软将内容相同的字符串视为同一个对象,如果您在此锁定了他,那么其实是锁定了同一个对象,将阻止其他地方对字符串的锁定。

lock(this)和lock(object)的比较

下面先看一个例子:创建一个窗体应用程序

首先是Oper类Oper.cs:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;namespace TestLockObject{    class Oper    {        private static object obj = new object();        private static Single slgTotal;        public string addition()        {            lock (obj)            <span style="color:#ff6666;">//lock(this)</span>            {                int i = 0;                slgTotal = 0;                for (i = 0; i <= 50; i++)                {                    slgTotal = slgTotal + i;                    Thread.Sleep(5);                }                return slgTotal.ToString() + " thread:" + Thread.CurrentThread.Name;            }        }    }}

其次是Form1.cs

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Threading;namespace TestLockObject{    public partial class Form1 : Form    {        delegate void SetTextCallback(string text);     // 设置委托        public Form1()        {            InitializeComponent();            //this.textBox.InvokeRequired = true;        }        /// <summary>        /// 利用委托设置文本框内容        /// </summary>        /// <param name="text"></param>        public void SetText(string text)        {            if (this.textBox.InvokeRequired)            {                SetTextCallback d = new SetTextCallback(SetText);                this.Invoke(d, new object[] { text });            }            else            {                this.textBox.Text = this.textBox.Text + "\r\n" + text;            }        }        private void buttonStart_Click(object sender, EventArgs e)        {            textBox.Text = "";            Thread[] thd = new Thread[500];            int i = 0;            for (i = 0; i < 50; i++)            {                thd[i] = new Thread(new ParameterizedThreadStart(thdText));                thd[i].Name = " Thread" + i.ToString();                thd[i].IsBackground = true;                thd[i].Start(i);            }        }        /// <summary>        /// 线程调用的方法        /// </summary>        /// <param name="obj"></param>        private void thdText(object obj)        {            Oper op = new Oper();            int i = Convert.ToInt32(obj);            SetText(op.addition());        }    }}
分析:lock(this) 锁定的对象是当前类实例,而每个线程操作的都是oper的新实例,lock(this)只对当前实例起作用,而 slgTotal 是类的静态变量,lock(this)实际上是没在起起我们想要的结果。而lock(obj)的实例,锁定的是静态变量object,所以能够锁定每个实例。

C#设计模式-单态模式
在《Design Patterns:Elements of Resuable Object-Oriented Software》中的定义是:Ensure a class only has one instance,and provide a global point of access to。它的主要特点不是根据客户程序调用生成一个新的实例,而是控制某个类型的实例数量-唯一一个。(《设计模式-基于C#的工程化实现及扩展》,王翔)。也就是说,单态模式就是保证在整个应用程序的生命周期中,在任何时刻,被指定的类只有一个实例,并为客户程序提供一个获取该实例的全局访问点。
经典模式:

public class Singleton{        private static Singleton instance;        private Singleton()        {                }        public static Singleton GetInstance()        {                if(instance==null)                {                        instance=new Singleton();                }                return instance;        }}
解析如下:
  1)首先,该Singleton的构造函数必须是私有的,以保证客户程序不会通过new()操作产生一个实例,达到实现单态的目的;
  2)因为静态变量的生命周期跟整个应用程序的生命周期是一样的,所以可以定义一个私有的静态全局变量instance来保存该类的唯一实例;
  3)必须提供一个全局函数访问获得该实例,并且在该函数提供控制实例数量的功能,即通过if语句判断instance是否已被实例化,如果没有则可以同new()创建一个实例;否则,直接向客户返回一个实例。
  在这种经典模式下,没有考虑线程并发获取实例问题,即可能出现两个线程同时获取instance实例,且此时其为null时,就会出现两个线程分别创建了instance,违反了单态规则。因此,需对上面代码修改。
多线程下的单态模式
1、Lazy模式
public class Singleton{       private static Singleton instance;       private static object _lock=new object();       private Singleton()       {       }       public static Singleton GetInstance()       {               if(instance==null)               {                      lock(_lock)                      {                             if(instance==null)                             {                                     instance=new Singleton();                             }                      }               }               return instance;       }}
上述代码使用了双重锁方式较好地解决了多线程下的单例模式实现。先看内层的if语句块,使用这个语句块时,先进行加锁操作,保证只有一个线程可以访问该语句块,进而保证只创建了一个实例。再看外层的if语句块,这使得每个线程欲获取实例时不必每次都得加锁,因为只有实例为空时(即需要创建一个实例),才需加锁创建,若果已存在一个实例,就直接返回该实例,节省了性能开销。
2、饿汉模式
这种模式的特点是自己主动实例。
public sealed class Singleton
{
        private static readonly Singleton instance=new Singleton();
 
        private Singleton()
        {
        }
        public static Singleton GetInstance()
        {
               return instance;
        }
}
上面使用的readonly关键可以跟static一起使用,用于指定该常量是类别级的,它的初始化交由静态构造函数实现,并可以在运行时编译。在这种模式下,无需自己解决线程安全性问题,CLR会给我们解决。由此可以看到这个类被加载时,会自动实例化这个类,而不用在第一次调用GetInstance()后才实例化出唯一的单态对象。

参考文章:

(1)http://www.cnblogs.com/yuqilin/archive/2011/10/15/2213766.html

(2)http://www.cnblogs.com/xun126/archive/2011/03/09/1970807.html

0 0