• C#中的lock关键字有何作用


      作为C#的程序员来说,在遇到线程同步的需求时最常用的就是lock关键字。但如何正确并有效地使用lock,却是能否高效地达到同步要求的关键。正因为如此,程序员需要完全理解lock究竟为程序做了什么。

      所涉及的知识点

    • lock的等效代码

    • System.Threading.Monitor类型的作用和使用方法

      分析问题

    1.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块内,以保证其最终退出同步。

    2.System.Threading.Monitor类型的作用和使用

      在前文中已经提到了,Monitor类型的Enter和Exit方法用来实现进入和退出对象的同步。具体来说,当Enter方法被调用时,对象的同步索引将被检查,并且.NET将负责一系列的后续工作,来保证对象访问时线程的同步,而Exit方法的调用,则保证了当前线程释放该对象的同步块。

      示例

      下列演示了自定义的类如何利用lock关键字(也就是Monitor类型)来实现线程同步,具体定义了一个包含需要同步执行方法的类型。

    /// <summary>

    /// 演示同步锁

    /// </summary>

    public class MyLock

    {

        //用来在静态方法中同步

        private static object o1 = new object();

        //用来在成员方法中不同

        private object o2 = new object();

        //成员变量

        private static int i1 = 0;

        private int i2 = 0;

        /// <summary>

        /// 测试静态方法的同步

        /// </summary>

        /// <param name=" handleObject ">同步时操作的对象</param>

        public static void Increment1(object handleObject)

        {

            lock (o1)

            {

                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 (o2)

            {

                Console.WriteLine("i2的值为:{0}", i2);

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

                Thread.Sleep(200);

                i2++;

                Console.WriteLine("i2自增后为:{0}", i2);

            }

        }

    }

    这样在main方法中,调用该类型对象的方法和其静态方法,测试其同步的效果。代码如下

    /// <summary>

    /// 程序入口

    /// </summary>

    class Program

    {

      /// <summary>

       /// 测试同步效果

       /// </summary>

      static void Main(string[] args)

      {

             //开始多线程

             Console.WriteLine("开始测试静态方法的同步");

             for (int i = 0; i < 5; i++)

             {

                 Task t = new Task(MyLock.Increment1, i);

                 t.Start();

             }

             //这里等待线程执行结束

            Thread.Sleep(3 * 1000);

             Console.WriteLine("开始测试成员方法的同步");

             MyLock myLock = new MyLock();

             //开始多线程

             for (int i = 0; i < 5; i++)

            {

                 Thread t = new Thread(myLock.Increment2);

                 t.Start();

             }

             Console.Read();

      }

    }

    下面是程序的执行结果:

    开始测试静态方法的同步

    i1的值为:0

    i1自增后为:1

    i1的值为:1

    i1自增后为:2

    i1的值为:2

    i1自增后为:3

    i1的值为:3

    i1自增后为:4

    i1的值为:4

    i1自增后为:5

    开始测试成员方法的同步

    i2的值为:0

    i2自增后为:1

    i2的值为:1

    i2自增后为:2

    i2的值为:2

    i2自增后为:3

    i2的值为:3

    i2自增后为:4

    i2的值为:4

    i2自增后为:5

       总结

      可以看到,线程同步被很好地保证了。这里需要强调的是,线程同步本身违反了多线程并行运行的原则,所以读者在使用线程同步时应该尽量做到把lock加在最小的程序块上。如果一个方法有大量的代码需要线程同步,那就需要重新考虑程序的设计了,是否真的有必要进行多线程处理,毕竟线程本身的开销也是相当大的。

      对静态方法的同步,一般采用静态私有的引用成员,而对成员方法的同步,一般采用私有的引用成员。读者需要注意静态和非静态成员的使用,都把同步对象申明为私有,这是保证线程同步高效并且正确的关键点。

    说明:以上内容是根据网上内容进行整理。

  • 相关阅读:
    MS SQL 挑战问题
    ORA00060错误
    用SYS本地登录或远程登录引起ORA01031错误
    RhelServer 5.5 安装ORACLE10
    sys不能远程登录的问题
    ORA12504:TNS:监听程序在CONNECT_DATA中未获得SERVICE_NAME
    MERGE INTO 性能问题疑问
    断开网线后监听服务器配置
    ORACLE——Instant Client配置SQL*LDR、EXP等命令工具
    监听服务管理
  • 原文地址:https://www.cnblogs.com/zwt-blog/p/4812627.html
Copyright © 2020-2023  润新知