你管這叫「執行緒安全」?
- 2021 年 4 月 30 日
- 筆記
- .NET, Asp.Net Core
來來來,面試八股文? 今天我們從什麼叫”執行緒安全”聊起?
今日份的乾糧:
- 什麼叫執行緒安全?
- 執行緒安全與變數的關係?
- 變數又與堆/棧/靜態存儲區有密切關係
什麼叫執行緒安全?
我們以常見的一行程式碼i++
,i--
為例, 電腦的操作姿勢可能與你想像的不一樣。
在大多數電腦中, 給變數自增並不是原子操作, 需要下面三步:
① 將變數值載入進暫存器
② 暫存器自增/自減值
③ 將暫存器值載入回原變數
多執行緒環境下,如果你不使用一些原子鎖操作:
執行緒A ( i++
)可能只執行了前面兩步後,之後CPU輪詢切換到其他執行緒或者執行緒A被搶佔CPU; 執行緒B ( i--
)欻欻執行完所有的三步;
當執行緒A重新獲得CPU,執行第三步, 一下子影響了執行緒B的執行預期。
上圖栩栩如生、動靜相宜地描述了 啥叫執行緒安全,這就是執行緒不安全! ☹️
你能遇到的問題,在平台這裡都不叫問題。
上面的問題可以使用原子鎖Interlocked
, //docs.microsoft.com/en-us/dotnet/api/system.threading.interlocked?view=net-5.0。
更多的關於執行緒安全的八股文 (請看這個,這個我面試騰訊考過,這個題目O了
(//www.cnblogs.com/JulianHuang/p/11765397.html))
執行緒安全? #變數#
執行緒安全的著力點,或者說問題的出發點是變數
— | — |
---|---|
棧區(stack) | 由編譯器自動分配釋放 ,存放函數的參數、局部變數 |
堆區(heap) | 一般由程式設計師分配釋放, 若程式設計師不釋放,程式可能不定時回收 |
全局區(靜態區)(static) | 全局變數和靜態變數的存儲是放在一塊的,程式結束後系統釋放 |
文字常量區 | 常量字元串就是放在這裡的。 程式結束後由系統釋放 |
程式程式碼區 | 存放函數體的二進位程式碼 |
using System;
public class StaticTest
{
static int count;
int number;
public StaticTest()
{
count = count + 1;
number = count;
}
public void display()
{
Console.WriteLine("object={0}:count={1}", number, count);
}
}
class MainTest
{
public static void Main()
{
StaticTest a = new StaticTest();
a.display();
}
}
- 不要認為
number
是值類型,就存儲在棧區 - 引用類型的值指向堆區(引用), 引用的值通常是32位或64位整形
- 局部變數的值存儲在棧區
1. 靜態成員:執行緒非安全
類的靜態變數即類變數,位於全局區(靜態區),為所有對象共享,一旦靜態變數被修改,其他對象對修改均可見,故執行緒非安全。
2.實例成員:單例模式(只有一個對象實例存在)執行緒非安全,非單例執行緒安全。
類的實例成員(非靜態成員)為實例所有,在堆中分配,若在系統中只存在一個此類實例,在多執行緒環境下,「猶如」靜態變數那樣,被某個執行緒修改後,其他執行緒對修改均可見,故執行緒非安全;
如果每個執行緒執行都是使用不同對象,那實例成員的修改將互不影響,故執行緒安全。
3. 局部變數:執行緒安全
每個執行緒執行時將會把局部變數放在各自棧幀的工作記憶體中,執行緒間不共享,故不存在執行緒安全問題。