有关多线程环境下的Volatile、lock、Interlocked和Synchronized们

本文探讨了多线程编程中volatile、Lock、Interlocked和Synchronized关键字的作用,解释了如何通过这些机制管理共享变量以防止数据竞争和意外行为,强调了在不同场景下的正确使用方法。
摘要由CSDN通过智能技术生成
  • 📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!

  • 📢本文作者:由webmote 原创

  • 📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔 !

序言

f2d7d392fec45a8537a2f076e8ed6af6.png
多线程下的变量访问,就如同脚踏几只船的海王,在其精细的时间管理下安排每一个女朋友约会,一不小心,就很可能打翻友谊的小船,彻底坠入无尽的大海深处…

而为了让各位亲爱的猿们,在约会对象之间横跳的时候,能优雅的控制住频率,编程语言引入了多个关键字和对象类完成相关操作。

让我们逐个看看,这些概念都能完成什么样的奇葩事件吧!

1. Volatile 修饰符关键字

volatile 关键字通常被用来表示一个字段的值很可能被多个线程修改,因此在编译器(VS)编译时不要进行优化,也不被缓存在编译器或硬件寄存器里。

volatile 关键字,确保每次读取和写入时,其值都是直接从内存中拿出来的,避免任何的优化和缓存。

volatile 关键字标识的信息,就如同海王的A女友信息,每次海王想知道A女友的信息时,都显示的是A的最新信息,而不是从其他人打探的过时信息。有了第一手的信息,才能最大程度的避免不慎翻船。

让我们来个模拟例子吧,由于编译器的优化,准备这个例子着实不易。

//让我们在.net6下测试下...
Console.WriteLine("开始测试...");
var test = new Test();
new Thread(delegate () { Thread.Sleep(500); test.foo = 255; }).Start();
while (test.foo != 255) ;
Console.WriteLine("不好了,A女友正在抵达战场!");
Console.ReadLine();

public class Test
{
    public int foo = 0;
}

如果你运行在Debug版本下,这时候你是可以收到A女友的抵达信息的。但是一旦你发布成Release,这个时候,命运的齿轮开始转动,你忽然收不到重要的抵达信息了,随着时间滴答滴答流动,危险的气息扑面而至。

你也试试看,切换到Release版本,按Ctrl+F5, 界面如下:

b0c93cc704354ffba75fe7eaf37b86cf.png
这个时候,volatile关键字的重要性就体现出来了,我们修改下如下信息:

public class Test
{
    public volatile int foo = 0;
}

b24b60e7bef8e6db4a304b7145815f1a.png
看吧,一个volatile,就救了你一条命。

volatile的使用注意事项:

  • volatile关键字通常用于多线程应用程序中,用于处理由多个线程同时访问的共享字段。

  • volatile不用于同步;它仅确保单个读取和写入操作的可见性和原子性。如果需要同步来强制执行顺序或互斥,请考虑使用其他同步机制,如lock,Monitor,Semaphore 。

  • 在多线程方案中处理共享数据时,通常建议使用lock 关键字或其他原子操作类,因为仅使用volatile关键字可能不足以满足复杂的同步要求。

  • volatile`关键字用于字段修饰,一般常用的是整型、布尔、指针,当然还有引用类型(一般指地址)

一般关闭线程的布尔值是最佳使用场景。

单例的双重检查锁场景也是有用的,例如:

public class Singleton {
private static volatile Singleton _instance = null; 
private static Object _locker = new Object();
public static Singleton GetSingleValue()
{
   if (_instance == null)
   {
       lock(_locker)
       {
          if (_instance == null)
          { _ instance = new Singleton(); }
       }
   }
   return _ instance;
}

当然,有更简单的写法,那就是利用Lazy

public class Singleton
{
     private static readonly Lazy<Singleton> _instance
         = new Lazy<Singleton>(() => new Singleton());
    private Singleton()
    {
    }
     public static Singleton Instance
     {
         get
         {
             return _instance.Value;
         }
     }
}

2. Lock 锁,锁住要锁的人

lock,是最好用的保护机制之一了。锁住资源,让其他线程都在后面排队,这样就不会撞到一块了。

5bfe33b299a75375ce74a42773df0bbd.png
话说,海王的日程表,必须有锁,没有锁的海王都死翘翘了。

这里是个简单的例子:

private object mylock = new object();

public int A {

  get {
    int result;
    lock(mylock) {
    result = mA; 
    }
    return result;
  } 

  set { 
     lock(mylock) { 
        mA = value; 
     }
  }
}

作为演示,这个例子足够简单;作为深度学习,这个例子并不好。

大部分类的属性都不需要lock操作,使用 public DateTime CreatedTime{get;set;}就已经足够了。因为基础类型都是原子操作的,因此没必要去锁定,除非你在get,set里有更复杂的操作。

因此,大可不必都增加上lock, 如果是多个线程访问,那么不妨增加上 volatile,当然,属性没法直接增加,有需要多写代码了。

3.Interlocked 非锁的原子操作

锁是独一无二的,那么对于时间管理大师们,来说,这并不是好消息。

那么有什么其他办法,既能满足大师们同时多个骚操作,又能正常而及时的得到通知呢?那就不得不提Interlocked 了,经济实惠,的确是居家旅行必备良词。

public class NuclearPowerPlant
{
	private long _meltdownIsHappening = 0;
	public bool MeltdownIsHappeningRightNow 
	{
		get
		{
			/* 锁定操作仅仅支持整型,那么我们使用它替换布尔。
			 */
			return Interlocked.Read(ref _meltdownIsHappening) == 1;
		}
		set
		{
			Interlocked.Exchange(ref _meltdownIsHappening, Convert.ToInt64(value));
		}
	}
}

这效率,嘎嘎的高。

注意 Interlocked.Increment(ref this.counter); 在实现上,等同于lock(this.locker) this. Counter++;,不过效率吗,那是翻了几倍。可惜的是好东西总有限制。Interlocked仅仅支持整数类型。

4. Synchronized 同步操作

Synchronized 关键字,总有点像从哪里抄过来的,因此,这个用法并不常见。
不过它的含义倒是很清晰,就是同一时刻仅允许一个线程访问。

代码如下:

public class Test
{
    public volatile int foo = 0;

    [MethodImpl(MethodImplOptions.Synchronized)]
    public int Add(int a)
    {
        return foo + a;
    }
}

MethodImpl(MethodImplOptions.Synchronized)这个属性的实现,也很简单,就是粗暴的lock(this)

因此,不建议直接使用。

3b38b9020e1ff5bd6b88b6d5f15dbc6e.png

总结

哦哦哦,好像意犹未尽,不过时间有限,先到此为止吧。

👓都看到这了,还在乎点个赞吗?

👓都点赞了,还在乎一个收藏吗?

👓都收藏了,还在乎一个评论吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值