头号码甲的错题本

头号码甲的错题本

你管这也叫线程安全?

最近大意了,竟然想将《面试官:实现一个带值变更通知能力的Dictionary》一文中的临界锁只应用到写操作。

不过我又快速清醒了,临界锁还真就得这么加。
临界锁的目的是保证这一段代码逻辑不会被打断。

假如只应用写锁:

某线程执行到写锁前(刚触发了一次变通通知),这时cpu时间片轮转或抢占, 切换到另外的线程又把这段代码执行了一次(因为字典key-value还没被前线程覆写),这样一次value变更实际执行了两次变通操作,这就悲剧了。


结合之间《你管这叫线程安全?》一文中多线程对于i++i--带来的线程不安全的理解。

你品你细品, 本次线程安全是在宏观代码行执行层面,
上次的i++ 是在微观寄存器层面, 归根到底还是要让多线程在多核环境下:代码逻辑不能被打断(代码执行节奏可能被打断)

多线程环境下,程序运行真是危机四伏。

微软官方怎么说?

还没完, 我还从微软官方原子操作找到一段话:

直译起来:
① bool char byte sbyte uint int float 和引用类型上的读写是原子操作;

② 由以上类型定义的枚举类型操作也是原子类型;

③ long ulong double decimal和用户定义类型上的读写不保证是原子操作;

④ 除了库文件本身设计了线程安全,一般况下下都不保证读写是原子操作, 这也包括i++i--


这段文字是不是刷新了某些童靴的认知(包括在下):

  1. 以后使用long num=8888;时要留个心眼,你也许会读到long类型的部分字节。

  2. 直译第①点说引用类型的读写是原子操作,第③点说用户类型不保证原子操作,但是大部分的用户类型是引用类型,这不互相矛盾了吗?

说说我的看法:

直译第①点中各种类型的读写操作是原子操作: 应该想表达式是纯粹的赋值、引用操作, 比如

int a =1;   				  //  赋值:  线程安全
Student s = new Student {}; 	          // 引用赋值: 线程安全
Student  s2= s;                           // 引用赋值: 线程安全

针对引用类型Dictionary的其他操作自然不是线程安全的。

依据这个思路, 第①③点就不矛盾了。


That's All, 本文依旧是#线程安全#、#锁# 这两个老生常谈的概念的延续, 我的知识体系也是在不断迭代更新,不断精炼。

09-15 01:49