浅谈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 后,代码如下图所示:
从上图被标注的区域可以看到:一条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);
}
}
结果如下:
同步与互斥 示例二:
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);
}
}
结果如下:
参考文章:
浅谈c#中使用lock的是与非 作者:Jeff Wong
同步技术之Monitor 作者:银河使者
作者: XuGang 网名:钢钢 |
出处: http://xugang.cnblogs.com |
声明: 本文版权归作者和博客园共有。转载时必须保留此段声明,且在文章页面明显位置给出原文连接地址! |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架