欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

c# lock 锁

程序员文章站 2022-06-05 20:25:35
lock语句 lock语句 lock 语句获取给定对象的互斥 lock,执行语句块,然后释放 lock。 持有 lock 时,持有 lock 的线程可以再次获取并释放 lock。 阻止任何其他线程获取 lock 并等待释放 lock。 为什么需要锁 为什么需要锁 作为C#的程序员来说,在遇到线程同步 ......
  • lock语句

lock 语句获取给定对象的互斥 lock,执行语句块,然后释放 lock。 持有 lock 时,持有 lock 的线程可以再次获取并释放 lock。 阻止任何其他线程获取 lock 并等待释放 lock。

 

  • 为什么需要锁

作为c#的程序员来说,在遇到线程同步的需求时最常用的就是lock关键字。lock 的目的很明确,就是不想让别人使用这段代码,体现在多线程情况下,只允许当前线程执行该代码区域,其他线程等待直到该线程执行结束;这样可以多线程避免同时使用某一方法造成数据混乱。

 

  • lock的等效代码

在.net的多线程程序中,经常会遇到lock关键字来控制同步,比如下列代码:

private object o = new object();

public void work()

{

  lock(o)

  {

    //做一些需要线程同步的工作

  }

}

事实上,lock这个关键字是c#为方便程序员而定义的语法,它等效于安全地使用system.threading.monitor类型。上面的代码就直接等效于下面的代码:

private object o = new object();

public void work()

{

  //这里很重要,是为了避免直接使用私有成员o,而导致线程不安全

  object temp = o;

  system.threading.monitor.enter(temp);

  try

  {

    //做一些需要线程同步的工作

  }

  finally

  {

    system.threading.monitor.exit(temp);

  }

}

 正如你看到的,真正实现了线程同步功能的,就是system.threading.monitor类型,lock关键字只是用来代替调用enter、exit方法,并且将所有的工作包含在try块内,以保证其最终退出同步。

注意:我们lock的一般是对象,不是值类型和字符串。

1、为什么不能lock值类型

    比如lock(1)呢?lock本质上monitor.enter,monitor.enter会使值类型装箱,每次lock的是装箱后的对象。lock 其实是类似编译器的语法糖,因此编译器直接限制住不能lock值类型。退一万步说,就算能编译器允许你lock(1),但是 object.referenceequals(1,1)始终返回false(因为每次装箱后都是不同对象),也就是说每次都会判断成未申请互斥锁,这样 在同一时间,别的线程照样能够访问里面的代码,达不到同步的效果。同理lock((object)1)也不行。

 2、lock字符串

    那么lock("xxx")字符串呢?msdn上的原话是:

锁定字符串尤其危险,因为字符串被公共语言运行库 (clr)“暂留”。 这意味着整个程序中任何给定字符串都只有一个实例,同一个对象表示了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序进程中的任何 位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。

 3、msdn推荐的lock对象

    通常,最好避免锁定 public 类型或锁定不受应用程序控制的对象实例。例如,如果该实例可以被公开访问,则 lock(this) 可能会有问题,因为不受控制的代码也可能会锁定该对象。这可能导致死锁,即两个或更多个线程等待释放同一对象。出于同样的原因,锁定公共数据类型(相比于 对象)也可能导致问题。

    而且lock(this)只对当前对象有效,如果多个对象之间就达不到同步的效果。

    而自定义类推荐用私有的只读静态对象,比如:

private static readonly object obj = new object();

为什么要设置成只读的呢?这是因为如果在lock代码段中改变obj的值,其它线程就畅通无阻了,因为互斥锁的对象变了,object.referenceequals必然返回false。

 

  •  lock 关键字锁定静态变量和非静态变量的区别

using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.threading;
using system.threading.tasks;

namespace testlock
{
    class program
    { 
        static void main(string[] args)
        { 
            console.writeline("----------开始测试单实例非静态锁----------");

            mylock mylock = new mylock(); 
            for (int i = 0; i < 5; i++)
            {
                thread t = new thread(mylock.increment1); 
                t.start(); 
            } 

            thread.sleep(3 * 1000);

            console.writeline("----------开始测试单实例静态锁----------");

            mylock mylock2 = new mylock(); 
            for (int i = 0; i < 5; i++)
            {  
                thread t = new thread(mylock2.increment2); 
                t.start(); 
            } 

            thread.sleep(3 * 1000); 

            console.writeline("----------开始测试多实例非静态锁----------");
             

            for (int i = 0; i < 5; i++)
            {
                mylock mlock = new mylock();
                thread t = new thread(mlock.increment1); 
                t.start(); 
            }  

            thread.sleep(3 * 1000); 

            console.writeline("----------开始测试多实例静态锁----------"); 

            for (int i = 0; i < 5; i++)
            {
                mylock mlock = new mylock();
                thread t = new thread(mlock.increment2); 
                t.start(); 
            }
 

            console.read();

        }
    } 

    public class mylock
    {
        //静态变量锁对象
        private readonly static object staticobj = new object();

        //非静态变量锁对象
        private readonly object obj = new object();

        //成员变量 
        private static int i1 = 0; 
        private static int i2 = 0;


        /// <summary>
        /// 非静态锁
        /// </summary>
        /// <param name="handleobject">要处理的对象</param>
        public  void increment1(object handleobject)
        { 
            lock (obj)
            { 

                console.writeline("i1的值为:{0}", i1);

                //这里刻意制造线程并行机会,来检查同步的功能

                thread.sleep(200);

                i1++;

                console.writeline("i1自增后为:{0}", i1);

            } 
        }

        /// <summary>
        /// 静态锁
        /// </summary>
        /// <param name="handleobject">要处理的对象</param>
        public void increment2(object handleobject)
        { 
            lock (staticobj)
            {

                console.writeline("i2的值为:{0}", i2);

                //这里刻意制造线程并行机会,来检查同步的功能

                thread.sleep(200);

                i2++;

                console.writeline("i2自增后为:{0}", i2);

            } 
        } 
        
    }
}

 

单实例非静态锁,线程没有并发(加锁成功);

c# lock 锁

单实例静态锁,线程没有并发(加锁成功);

c# lock 锁

多实例非静态锁,线程并发(加锁失败);

c# lock 锁

多实例静态锁,线程没有并发(加锁成功)

c# lock 锁

 

说明:以上内容是根据网上内容进行整理,并加以归纳。