多線程環境下,程序運行真是危機四伏

姿勢在不斷的更新迭代, 太卷了。

你管這也叫線程安全?

最近大意了,竟然想將《面試官:實現一個帶值變更通知能力的Dictionary》一文中的臨界鎖只應用到寫操作。

內心旁白:
讀操作又不會修改數據,無論是新值還是舊值,反正能讀到。

不過我又快速清醒了,臨界鎖還真就得這麼加。
臨界鎖的目的是保證這一段代碼邏輯不會被打斷。

假如只應用寫鎖:

某線程執行到寫鎖前(剛觸發了一次變通通知),這時cpu時間片輪轉或搶佔, 切換到另外的線程又把這段代碼執行了一次(因為字典key-value還沒被前線程覆寫),這樣一次value變更實際執行了兩次變通操作,這就悲劇了。


結合之間《你管這叫線程安全?》一文中多線程對於i++i--帶來的線程不安全的理解。

你品你細品, 本次線程安全是在宏觀代碼行執行層面,
上次的i++ 是在微觀寄存器層面, 歸根到底還是要讓多線程在多核環境下:代碼邏輯不能被打斷(代碼執行節奏可能被打斷)

多線程環境下,程序運行真是危機四伏。

微軟官方怎麼說?

還沒完, 我還從微軟官方原子操作找到一段話:

Reads and writes of the following data types are atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list are also atomic. Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, are not guaranteed to be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement.

直譯起來:
① bool char byte sbyte uint int float 和引用類型上的讀寫是原子操作;

② 由以上類型定義的枚舉類型操作也是原子類型;

③ long ulong double decimal和用戶定義類型上的讀寫不保證是原子操作;

④ 除了庫文件本身設計了線程安全,一般況下下都不保證讀寫是原子操作, 這也包括i++i--


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

  1. 以後使用long num=8888;時要留個心眼,你也許會讀到long類型的部分位元組。

  2. 直譯第①點說引用類型的讀寫是原子操作,第③點說用戶類型不保證原子操作,但是大部分的用戶類型是引用類型,這不互相矛盾了嗎?

我向微軟官方提出了我的這個疑問,有興趣可以關注這個github issue

說說我的看法:

直譯第①點中各種類型的讀寫操作是原子操作: 應該想表達式是純粹的賦值、引用操作, 比如

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

針對引用類型Dictionary的其他操作自然不是線程安全的。

依據這個思路, 第①③點就不矛盾了。


That’s All, 本文依舊是#線程安全#、#鎖# 這兩個老生常談的概念的延續, 我的知識體系也是在不斷迭代更新,不斷精鍊。

我也沒想到越挖越深,真的是太卷了,如有不同的看法,請留言交流。

Tags: