浅谈C# 中的lock 方法与Monitor 类的关系_以及同步与互斥

简介:

这是一个旧瓶新装的话题。只是将我今日的所见所思予以实践和整理,以备往后所用。同时也提供给大家,希望能有所帮助。

A 从单例模式说起

代码如下:

class Program
{
 static void Main(string[] args)
 {
 Singleton demo1 = Singleton.Init();
 Singleton demo2 = Singleton.Init();
 }
}
 
public class Singleton
{
 private Singleton() { }
 
 private static Singleton instance = null;
 private static readonly object singleObj = new object();
 
 public static Singleton Init()
 {
 if (null == instance)
 {
 lock (singleObj) //singleObj 不能使用instance 代替
 {
 if (null == instance)
 {
 instance = new Singleton();
 }
 }
 }
 return instance;
 }
}

关于单例模式,大家可以参考:http://terrylee.cnblogs.com/archive/2005/12/09/293509.html

B 关于lock 方法

在以上单列模式的代码中:

如果将: private static readonly object singleObj = new object();
修改为: private static readonly object singleObj = null;

将在lock( ) 方法处,抛出未处理的异常:System.ArgumentNullException: 值不能为空!

● lock 关键字将语句块标记为临界区

lock( obj ) 方法中的obj 对象为:获取排他锁的指定对象

obj 对象为null 将导致 ArgumentNullException 异常。

lock 方法在MSIL 中会被编译成 Monitor.Enter( ) 和 Monitor.Exit( ) 。例如:

public static void MyLock()
{
 lock (typeof(Program))
 {
 }
}

以上代码通过lock 语句使MyLock 同步,这个方法被编译成MSIL 后,代码如下图所示:

image

从上图被标注的区域可以看到:一条lock 语句被编译成了调用Monitor 的Enter 和Exit 的方法。
lock 的功能就相当于直接调用Monitor 的Entry 方法,并在结束后会自动调用Monitor 的Exit 方法解除锁定。

C 关于Monitor 类

Monitor 类属于System.Threading 命名空间;

Monitor 类提供同步对对象的访问的机制;

使用 Monitor 锁定对象(即引用类型)而不是值类型;

Monitor 类型对于多线程操作是安全的;

Monitor 类的示例一:

class Program2
{
 static void Main(string[] args)
 {
 MyMonitor1 mon_1 = new MyMonitor1();
 mon_1.Test();
 }
}
 
class MyMonitor1
{
 private object obj = new object();
 
 public void Test() 
 {
 //开始锁定
 System.Threading.Monitor.Enter(obj);
 try
 {
 //lock 的区域
 }
 catch (Exception e)
 {
 // 异常处理代码
 }
 finally
 {
 //解除锁定
 System.Threading.Monitor.Exit(obj);
 }
 }
}

Monitor 类的示例二:

class Program3
{
 static void Main(string[] args)
 {
 //多个线程调用Test方法
 Thread t1 = new Thread(MyMonitor2.Test);
 Thread t2 = new Thread(MyMonitor2.Test);
 t1.Start();
 t2.Start();
 }
}
class MyMonitor2
{
 private static object obj = new object();
 
 public static void Test()
 {
 //使用TryEntry方法设置一个锁定超时
 if (Monitor.TryEnter(obj, 2000))
 {
 try
 {
 Console.WriteLine("等待4秒开始");
 Thread.Sleep(4000);
 Console.WriteLine("等待4秒结束");
 }
 finally
 {
 //解除锁定
 Monitor.Exit(obj);
 }
 }
 else 
 {
 Console.WriteLine("已超时2秒!");
 }
 
 }
}

D 关于同步与互斥

关于同步的问题,可以使用Monitor 类来解决。

在使用Monitor 类的时候,建议将Monitor.Enter( ) 方法替换成Monitor.TryEnter( ) 方法。

使用Monitor.Enter( ) 方法时,代码如下:

Monitor.Entry(lockObj);
try
{
 // lockObj的同步区
}
catch(Exception e)
{
 // 异常处理代码
}
finally
{
 Monitor.Exit(lockObj); // 解除锁定
}

注意:如果直接在C#源程序中使用Monitor类,就必须调用Exit方法来显式地解除锁定。

使用Monitor.TryEnter( ) 方法时,代码如下:

if(Monitor.TryEntry(lockObj, 1000))
{
 try
 {
 }
 finally
 {
 Monitor.Exit(lockObj);
 }
}
else
{
 // 超时后的处理代码
}

注意:使用TryEntry方法设置一个锁定超时,单位是毫秒。

上面的代码设置了锁定超时时间为1秒。

如果在1秒钟之内,lockObj 还未被解锁,TryEntry 方法就会返回 false;

如果在1秒钟之内,lockObj 被解锁,TryEntry 方法就会返回 true。

这样,可以使用TryEntry 方法来避免死锁。

同步与互斥 示例一:

class Program4
{
 static void Main(string[] args)
 {
 Thread A = new Thread(TestClass1.GetA);
 A.Name = "Thread_A ";
 Thread B = new Thread(TestClass1.GetB);
 B.Name = "Thread_B ";
 A.Start();
 B.Start();
 }
}
 
class TestClass1
{
 private static object resource_A = new object();
 private static object resource_B = new object();
 
 public static void GetA()
 {
 MyWrite("in GetA()");
 if (Monitor.TryEnter(resource_A, 2000))
 {
 MyWrite("get resource_A");
 GetB();
 Thread.Sleep(2000);
 Monitor.Exit(resource_A);
 MyWrite("exit resource_A");
 }
 else
 {
 MyWrite("no has resource_A");
 }
 }
 
 public static void GetB()
 {
 MyWrite("in GetB()");
 if (Monitor.TryEnter(resource_B, 1000))
 {
 MyWrite("get resource_B");
 GetA();
 Thread.Sleep(1000);
 Monitor.Exit(resource_B);
 MyWrite("exit resource_B");
 }
 else
 {
 MyWrite("no has resource_B");
 }
 }
 
 //自定义打印方法
 private static void MyWrite(string str)
 {
 Console.WriteLine(Thread.CurrentThread.Name + str);
 }
}

结果如下:

TryEnter1

同步与互斥 示例二:

class Program5
{
 static void Main(string[] args)
 {
 Thread A = new Thread(TestClass2.GetA);
 A.Name = "Thread_A ";
 Thread B = new Thread(TestClass2.GetB);
 B.Name = "Thread_B ";
 A.Start();
 B.Start();
 }
}
 
class TestClass2
{
 //排他锁的对象 A
 private static object resource_A = new object();
 //排他锁的对象 B
 private static object resource_B = new object();
 
 public static void GetA()
 {
 MyWrite("in GetA()");
 if (Monitor.TryEnter(resource_A, 1000))
 {
 MyWrite("get resource_A");
 Thread.Sleep(1000);
 //GetB();
 if (Monitor.TryEnter(resource_B, 2000))
 {
 Monitor.Exit(resource_B);
 }
 Monitor.Exit(resource_A);
 MyWrite("exit resource_A");
 }
 else
 {
 MyWrite("no has resource_A");
 }
 }
 
 public static void GetB()
 {
 MyWrite("in GetB()");
 if (Monitor.TryEnter(resource_B, 1000))
 {
 MyWrite("get resource_B");
 Thread.Sleep(1000);
 //GetA();
 if (Monitor.TryEnter(resource_A, 2000))
 {
 Monitor.Exit(resource_A);
 }
 Monitor.Exit(resource_B);
 MyWrite("exit resource_B");
 }
 else
 {
 MyWrite("no has resource_B");
 }
 }
 
 //自定义打印方法
 private static void MyWrite(string str)
 {
 Console.WriteLine(Thread.CurrentThread.Name + str); 
 }
}

结果如下:

TryEnter2

参考文章:

浅谈c#中使用lock的是与非 作者:Jeff Wong

同步技术之Monitor 作者:银河使者




本文转自钢钢博客园博客,原文链接:http://www.cnblogs.com/xugang/archive/2010/11/05/1869818.html,如需转载请自行联系原作者

相关文章
|
2月前
|
开发框架 .NET C#
C#|.net core 基础 - 删除字符串最后一个字符的七大类N种实现方式
【10月更文挑战第9天】在 C#/.NET Core 中,有多种方法可以删除字符串的最后一个字符,包括使用 `Substring` 方法、`Remove` 方法、`ToCharArray` 与 `Array.Copy`、`StringBuilder`、正则表达式、循环遍历字符数组以及使用 LINQ 的 `SkipLast` 方法。
|
3月前
|
存储 C# 索引
C# 一分钟浅谈:数组与集合类的基本操作
【9月更文挑战第1天】本文详细介绍了C#中数组和集合类的基本操作,包括创建、访问、遍历及常见问题的解决方法。数组适用于固定长度的数据存储,而集合类如`List<T>`则提供了动态扩展的能力。文章通过示例代码展示了如何处理索引越界、数组长度不可变及集合容量不足等问题,并提供了解决方案。掌握这些基础知识可使程序更加高效和清晰。
93 2
|
3月前
|
开发框架 .NET 程序员
C# 去掉字符串最后一个字符的 4 种方法
在实际业务中,我们经常会遇到在循环中拼接字符串的场景,循环结束之后拼接得到的字符串的最后一个字符往往需要去掉,看看 C# 提供了哪4种方法可以高效去掉字符串的最后一个字符
371 0
|
2月前
|
编译器 C#
C#多态概述:通过继承实现的不同对象调用相同的方法,表现出不同的行为
C#多态概述:通过继承实现的不同对象调用相同的方法,表现出不同的行为
130 65
|
1月前
|
JSON 程序员 C#
使用 C# 比较两个对象是否相等的7个方法总结
比较对象是编程中的一项基本技能,在实际业务中经常碰到,比如在ERP系统中,企业的信息非常重要,每一次更新,都需要比较记录更新前后企业的信息,直接比较通常只能告诉我们它们是否指向同一个内存地址,那我们应该怎么办呢?分享 7 个方法给你!
|
1月前
|
C# UED SEO
C# 异步方法async / await任务超时处理
通过使用 `Task.WhenAny`和 `Task.Delay`方法,您可以在C#中有效地实现异步任务的超时处理机制。这种方法允许您在指定时间内等待任务完成,并在任务超时时采取适当的措施,如抛出异常或执行备用操作。希望本文提供的详细解释和代码示例能帮助您在实际项目中更好地处理异步任务超时问题,提升应用程序的可靠性和用户体验。
74 3
|
2月前
|
存储 C#
【C#】大批量判断文件是否存在的两种方法效率对比
【C#】大批量判断文件是否存在的两种方法效率对比
52 1
|
2月前
|
Java 程序员 C#
【类的应用】C#应用之派生类构造方法给基类构造方法传参赋值
【类的应用】C#应用之派生类构造方法给基类构造方法传参赋值
16 0
|
2月前
|
C#
C#的方法的参数传递
C#的方法的参数传递
31 0
|
2月前
|
数据可视化 程序员 C#
C#中windows应用窗体程序的输入输出方法实例
C#中windows应用窗体程序的输入输出方法实例
56 0