C#中的lock关键字有何做用

  做为C#的程序员来讲,在遇到线程同步的需求时最经常使用的就是lock关键字。但如何正确并有效地使用lock,倒是可否高效地达到同步要求的关键。正由于如此,程序员须要彻底理解lock究竟为程序作了什么。程序员

  所涉及的知识点安全

• lock的等效代码多线程

• System.Threading.Monitor类型的做用和使用方法测试

  分析问题线程

1.lock的等效代码设计

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

private object o = new object();索引

public void Work()rem

{同步

  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加在最小的程序块上。若是一个方法有大量的代码须要线程同步,那就须要从新考虑程序的设计了,是否真的有必要进行多线程处理,毕竟线程自己的开销也是至关大的。

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

 

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

相关文章
相关标签/搜索