你管这也叫线程安全?
最近大意了,竟然想将《面试官:实现一个带值变更通知能力的Dictionary》一文中的临界锁只应用到写操作。
不过我又快速清醒了,临界锁还真就得这么加。
临界锁的目的是保证这一段代码逻辑不会被打断。
假如只应用写锁:
某线程执行到写锁前(刚触发了一次变通通知),这时cpu时间片轮转或抢占, 切换到另外的线程又把这段代码执行了一次(因为字典key-value还没被前线程覆写),这样一次value变更实际执行了两次变通操作,这就悲剧了。
结合之间《你管这叫线程安全?》一文中多线程对于i++
、i--
带来的线程不安全的理解。
你品你细品, 本次线程安全是在宏观代码行执行层面,
上次的i++
是在微观寄存器层面, 归根到底还是要让多线程在多核环境下:代码逻辑不能被打断(代码执行节奏可能被打断)。
多线程环境下,程序运行真是危机四伏。
微软官方怎么说?
还没完, 我还从微软官方原子操作找到一段话:
直译起来:
① bool char byte sbyte uint int float 和引用类型上的读写是原子操作;
② 由以上类型定义的枚举类型操作也是原子类型;
③ long ulong double decimal和用户定义类型上的读写不保证是原子操作;
④ 除了库文件本身设计了线程安全,一般况下下都不保证读写是原子操作, 这也包括i++
、i--
这段文字是不是刷新了某些童靴的认知(包括在下):
以后使用
long num=8888;
时要留个心眼,你也许会读到long类型的部分字节。直译第①点说引用类型的读写是原子操作,第③点说用户类型不保证原子操作,但是大部分的用户类型是引用类型,这不互相矛盾了吗?
说说我的看法:
直译第①点中各种类型的读写操作是原子操作: 应该想表达式是纯粹的赋值、引用操作, 比如
int a =1; // 赋值: 线程安全
Student s = new Student {}; // 引用赋值: 线程安全
Student s2= s; // 引用赋值: 线程安全
针对引用类型Dictionary的其他操作自然不是线程安全的。
依据这个思路, 第①③点就不矛盾了。
That's All, 本文依旧是#线程安全#、#锁# 这两个老生常谈的概念的延续, 我的知识体系也是在不断迭代更新,不断精炼。