6. 鎖

来源:https://www.cnblogs.com/jjfan0327/archive/2020/04/18/12720850.html
-Advertisement-
Play Games

開發多用戶、資料庫驅動的應用時,最大的難點是:一方面要最大程度的利用資料庫的併發訪問,一方面還要確保每個用戶能以一致的方式讀取和修改數據,為此有了鎖的機制。 6.1 什麼是鎖 鎖機制用於管理對共用資源的併發訪問。InnoDB除了會在行級別上對錶數據上鎖,也會在資料庫內部其他多個地方上鎖,從而允許對多 ...


開發多用戶、資料庫驅動的應用時,最大的難點是:一方面要最大程度的利用資料庫的併發訪問,一方面還要確保每個用戶能以一致的方式讀取和修改數據,為此有了鎖的機制。

6.1 什麼是鎖

鎖機制用於管理對共用資源的併發訪問。InnoDB除了會在行級別上對錶數據上鎖,也會在資料庫內部其他多個地方上鎖,從而允許對多種不同資源提供併發訪問。例如,操作緩衝池中的LRU列表,刪除、添加移動LRU列表上的元素等。

InnoDB 鎖的實現和 Oracle資料庫非常類似,提供一致性的非鎖定讀、行級鎖支持,行級鎖沒有相關的額外開銷,並可以得到併發性和一致性。

6.2 lock 與 latch(閂)

latch 一般稱為閂鎖(輕量級),因為其要求鎖定的時間非常短,若持續時間長,性能會很差。在 InnoDB中,latch 又可以分為 mutex 和rwlock (讀寫鎖)。目的是用來保證併發線程操作臨界資源的正確性,並且通常沒有死鎖檢驗。

lock 一般鎖定的是資料庫中的對象,如表、頁、行。並且lock的對象僅在事務 commit 或 rollback後釋放。

  lock latch
對象 事務 線程
保護 資料庫記憶體 記憶體數據結構
持續時間 整個事務過程 臨界資源
模式 行鎖、表鎖、意向鎖 讀寫鎖、互斥鎖
死鎖 通過 wait for graph、time out 機制進行死鎖檢測與處理 無死鎖檢測與處理機制,僅通過應用程式加鎖的順序保證無死鎖的發生
存在於 lock Manager的哈希表中 每個數據結構的對象中

6.3 InnoDB存儲引擎中的鎖

6.3.1 鎖的類型

InnoDB 實現了以下兩種標準的行級鎖:

  1. 共用鎖,S Lock,允許事務讀一行數據
  2. 排它鎖, X Lock,允許事務刪除或更新一行數據

S和X 鎖都是行鎖,相容是指對同一行記錄的相容性情況。

InnoDB 支持多粒度鎖定,這種鎖定支持行級別的鎖和表級別的鎖同時存在,即意向鎖,意向鎖是將鎖定的對象分為多個層次,意向鎖意味著事務希望在更細粒度上進行加鎖。

如果需要對頁上的記錄r加X鎖,需要分別對資料庫A、表、頁上意向鎖IX,最後對r上X鎖。若其中任意一個部分導致等待,那麼該操作需要等待粗粒度的鎖完成。InnoDB支持意向鎖比較簡單,其意向鎖就是表級別的鎖,主要是為了在一個事務中揭示下一行將被請求的鎖類型。

  1. 意向共用鎖(IS Lock),事務想要獲得表中某幾行的共用鎖
  2. 意向排它鎖(IX Lock),事務想要獲得表中某幾行的排它鎖

InnoDB支持的是行級別的鎖,因此意向鎖不會阻塞全表掃描以外的任何請求。

  IS IX S X
IS 相容 箭容 相容 不相容
IX 相容 相容 不相容 不相容
S 相容 不相容 相容 不相容
X 不相容 不相容 不相容 不相容

6.3.2 一致性非鎖定讀

一致性的非鎖定讀(consistent nonlocking read)是指 InnoDB 通過多版本併發控制(multi versioning Concurrency control)的方式來讀取當前執行時間資料庫中行的數據。如果此時,讀取的行正在執行 delete 或 update,這時讀取操作不會因此等待行上的鎖釋放,相反,InnoDB會去讀取行的一個快照數據(該實現是通過 undo 段來完成),而undo段是用來在事務中回滾數據,因此沒有額外開銷,此外,讀取快照數據是不需要上鎖的,因為沒有事務需要對歷史數據進行修改

一致性的非鎖定讀,是InnoDB的預設讀取方式(Repeatable隔離級別),即讀取不會占用和等待表上的鎖。

一個行記錄不止一個快照數據,一般稱這種技術為行多版本併發控制技術,在事務隔離級別為 READ COMMITED 和 REPEATABLE READ下,InnoDB使用的是非鎖定的一致性讀。

在 READ COMMITED事務隔離級別下,對於快照數據總是讀取被鎖定行的最新一份的快照數據,從資料庫理論的角度看,違反了ACID中的隔離性I。而 REPEATABLE READ 讀取的是事務開始時的行數據版本。

時間 會話A 會話B
1 BEGIN  
2 select * from t where id=1  
3   BEGIN
4  

update t set id=3 where id=1

此時,事務沒有提交,加X鎖,會話A的查詢5,使用MVCC查詢結果

5 select * from t where id=1  
6   COMMIT;
7 select * from t where id=1  
8 COMMIT;  

在 READ COMMITED 隔離級別下,1,5 返回id=1的數據,7返回null,因為會話B提交後,id=1的數據被更新為3,此時的數據是最新的。而在REPEATABLE READ 下,1,5,7 總是返回id=1 的數據,因為總是讀取事務開始前的數據。

6.3.3 一致性鎖定讀

某些情況下,用戶需要顯式的對資料庫讀取操作進行加鎖以保證數據的一致性。而這要求資料庫支持加鎖語句,即使是對於select 的查詢語句。InnoDB對於 select語句支持兩種一致性的鎖定讀操作:

  1. select …… for update
  2. select …… lock in share mode

select …… for update 對讀取的行加X鎖,其他事務不能再對該行加任何鎖。select …… share in mode 對讀取的行記錄加S鎖,其他事務可以向被鎖定的行加S鎖,但不能加X鎖,X鎖只能阻塞。

 select …… for update 和 select …… share in mode必須在事務中,事務提交,鎖釋放。

6.3.4 自增長與鎖

在 InnoDB的記憶體結構中,對每個含有自增長值的表都有一個自增長計數器。執行如下的語句得到計數器的值:

select MAX(auto_inc_col) from t for update;

  插入操作會根據這個自增長的計數器值加1 賦予自增長列。這個實現方式被稱為 AUTO-INC Locking、這種鎖其實採用一種特殊的表鎖機制,為了提高插入性能,鎖不是在事務完成後釋放,而是完成對自增長值的插入的SQL語句後立即釋放。

  雖然 AUTO-INC Locking在一定程度上提高了併發性,但對於有自增長值的列的併發插入性能較差,事務必須等待前一個插入的完成(雖然不需要等待事務的完成),其次,對於 Insert…… select 的大數據量的插入會影響插入的性能,因為另一個事務會被阻塞。

  MySQL 5.1.22 版本開始,InnoDB提供了一種輕量級互斥量的自增長的實現機制。從該版本開始,InnoDB提供了一個參數 innodb_autoinc_lock_mode來控制自增長的模式,該參數預設為1.

innodb_autoinc_lock_mode 說明
0 MySQL5.1.22版本之前的實現方式,即通過 AUTO-INC lock方式,因為有了新的自增長方式,所以該值不應該是用戶的首選項
1

預設值,對於”simple inserts“,該值會使用互斥量對記憶體中的計數器進行累加的操作。對於”bulk inserts“,還是使用傳統的 AUTO-INC locking方式。在這種配置下,如果不考慮回滾操作,對於自增值的增長還是連續的。並且在這種方式想,statement-based 方式的replication還是能很好的工作。

註:如果已經使用 AUTO-INC locking方式,而這時需要再進行”simple inserts“操作時,還是需要等待”AUTO-INC locking“的釋放

2 在該模式下,對所有的”insert-likes“自增長值的產生都是通過互斥量,而不是 AUTO-INC locking,顯然,這是性能最高的方式,然而,會帶來一些問題,因為併發的存在,在每次插入時,自增長的值可能不是連續的,此外,重要的是,基於 Statement-based Replication會有問題。因此,使用這個模式,任何時候都應該使用 row-base replication,這樣才能保證最大的併發及 replication主從數據的一致性。

 

插入類型 說明
insert-like 所有的插入語句,包括 insert、replace、insert……select、replace……select、load data等
simple inserts 能在插入前就確定插入行數的語句,包括insert,replace等,不包含 insert …… on duplicate key update這類SQL
bulk inserts 在插入前不能確定插入行數的語,如 insert……select,replace……select、load data等
mixed-mode inserts 插入中有一部分的值是自增長的,一部分是可以確定的,如insert into t (e1,e2) values (1,'a'),(NULL,'b'),(3,'c'),也可以是 insert …… on duplicate key update這類SQL 

另外,在 InnoDB中,自增長的列必須是索引,同時是索引的第一個列,否則MySQL會拋出異常。

6.3.5 外鍵和鎖

外鍵主要用於引用完整性的約束檢查,在InnoDB中,對於一個外鍵列,如果沒有顯示的對這個列添加索引,InnoDB會自動對其加索引,避免表鎖。

對於外鍵的插入或更新,需要先查詢父表,但是對於父表的查詢不是使用一致性非鎖定讀(MVCC),因為這樣會發生數據不一致的問題,使用的是 select ……lock in share mode,主動為付表加S鎖,如果這時父表上已有X鎖,則阻塞。

時間 會話A 會話B
1 BEGIN  
2 delete from parent where id=5  
3   BEGIN
4  

insert into child select 2,5

#第二列是外鍵,執行時被阻塞waiting

6.4 鎖的演算法

6.4.1 行鎖的三種演算法

  InnoDB 有三種行鎖演算法,分別是

  1. Record Lock:單個記錄上的鎖
  2. Gap Lock:間隙鎖,鎖定一個範圍,但不包含記錄本身
  3. Next-Key Lock:Gap Lock + Record Lock,鎖定一個範圍,並鎖定記錄本身

Record Lock 總是鎖住索引記錄,如果 InnoDB在建立時沒有設置任何一個索引,那麼 InnoDB會使用隱士的主鍵來鎖定。

InnoDB對於行的查詢都是採用 Next-Key Lock,其目的是為瞭解決幻讀 Phantom Problem,是謂詞鎖的一種改進。

當查詢的索引含有唯一屬性時,InnoDB 會對Next-Key Lock優化,降級為 Record Key,以此提高應用的併發性。

如前面介紹的。next-key lock 降級為record lock是在查詢的列是唯一索引的情況下,若是輔助索引,則情況不同:

create table z (a int, b int , Primary key(a), key(b))
insert into z select 1,1;
insert into z select 3,1;
insert into z select 5,3;
insert into z select 7,6;
insert into z select 10,8;

  其中b是輔助索引,此時執行 select * from z where b=3 for update;

  此時,sql 語句通過索引列b進行查詢,因此其使用傳統的 next-key lock 進行加鎖,並且由於有兩個索引,其需要分別進行鎖定。對於聚集索引,僅對列a=5的索引加record lock。而輔助索引,加的是next-key lock,鎖定的是(1,3)範圍,特別註意的是,InnoDB還會對輔助索引下一個鍵值加上 gap lock,即還有一個(3,6)範圍的鎖。因此,一下SQL都會被阻塞:

select * from z where a=5 lock in share mode;
insert into z select 4,2;
insert into z select 6,5;

  從上面可以看出,gap lock的作用是為了阻止多個事務將記錄插入到同一個範圍內,而這會導致幻讀問題的產生。用戶可以通過以下兩種方式來關閉 Gap Lock:

  1. 將事務隔離級別改為 READ COMMITED 
  2. 將參數 innodb_locks_unsafe_for_binlog設為1

在上述的配置下,除了外鍵約束和唯一性檢查依然需要的Gap Lock,其餘情況僅使用 Record Lock進行鎖定,需要牢記的是,上述配置破壞了事務的隔離性,並且對 replication可能會導致不一致。且從性能上看,READ COMMITED也不會優於預設的 READ REPEATABLE;

在 InnoDB中,對Insert的操作,其會檢查插入記錄的下一條記錄是否被鎖定,若已鎖定,則不允許查詢。對於上面的例子,會話A已經鎖定了表中b=3的記錄,即已鎖定了(1,3)的範圍,這時如果在其他會話中進行如下的插入同樣會導致阻塞

insert into z select 2,2;

  因為檢測到輔助索引列b上插入2的記錄時,發現3已經被索引,而將插入修改為如下值,可以立即執行:

insert into z select 2,0;

  最後,對於唯一鍵值的鎖定,next-key lock降級為record ke僅存在於查詢所有的唯一索引列。若唯一索引由多個列組成,而查詢僅是查找多個唯一索引列中的一個,那麼查詢其實是range類型,而不是point查詢,故InnoDB依然採用 next-key lock進行鎖定

6.4.2 解決 Phantom Problem

幻讀指的是在同一個事務下,連續執行兩次相同的SQL語句可能返回不一樣的結果,第二次的SQL語句可能會返回之前不存在的行。

InnoDB採用 next-key lock 的演算法解決了 Phantom Problem,對 select * from t where id > 2 for update,鎖住的不單是5這個值,而是對(2,+∞)這個範圍加了X鎖。因此,對這個範圍的插入是不允許的,從而避免幻讀。

時間 會話A 會話B
1

set session

tx_isolation = 'READ-COMMITED'

 
2 BEGIN  
3

select * from t where a>2 for update;

***********1 row *************

a:4

 
4   BEGIN
5   insert into t select 4
6   COMMIT;
7

select * from t where a>2 for update;

***********1 row *************

a:4

***********2 row *************

a:5

 

REPEATABLE  READ 採用的是 next-key locking加鎖。而 READCOMMITED 採用的是 record lock .

此外,用戶可以通過 InnoDB的 next-key lock在應用層面實現唯一性的檢查:

select * from table where col=xxx lock in share mode;
if not found any row :
#unique for insert value
insert into table values(……);

  如果用戶通過一個索引查詢一個值,並對該行加上了S lock,那麼即使查詢的值不存在,其鎖定的也是一個範圍,因此若沒有返回任何行,那麼新插入的值一定是唯一的。

那,如果在第一步select lock in share mode時,有多個事務併發操作,那麼這種唯一性檢查是否會有問題,其實不會,因為會發生死鎖。只有一個事務會成功,其他的事務會拋出死鎖錯誤

6.5 鎖問題

6.5.1 臟讀

臟數據是指未提交的數據,如果讀到了臟數據,即一個事務可以讀到另一個事務未提交的數據,則顯然違反了資料庫的隔離性。

臟讀指的是在不同事務下,當前事務可以讀到另外事務未提交的數據,即臟數據。

時間 會話A 會話B
1

set 

@@tx_isolation = 'read-ncommited'

 
2  

set 

@@tx_isolation = 'read-ncommited'

3  

BEGIN

4  

select * from t ;

**********1 row *************

a:1

5 insert into t select 2;  
6  

select * from t ;

**********1 row *************

a:1

**********2 row *************

a:2

臟讀發生條件是需要事務的隔離級別為 read uncommited;目前大部分資料庫至少設置為 read COMMITED;

6.5.2 不可重覆讀

不可重覆讀和臟讀的區別是:臟讀是讀到未提交的數據,而不可重覆讀讀到的是已提交的數據,但是違反了事務一致性的要求。

時間 會話A 會話B
1

et 

@@tx_isolation = 'read-commited'

 
2  

et 

@@tx_isolation = 'read-commited'

3 BEGIN BEGIN
4

select * from t ;

**********1 row *************

a:1

 
5   insert into t select 2;
6   COMMITED
7

select * from t ;

**********1 row *************

a:1

**********2 row *************

a:2

 

 

一般來說,不可重覆讀是可接受的,因為讀到的是已提交的數據,本身沒有帶來很大問題。在 InnoDB中使用 next-key lock避免不可重覆讀問題,即 幻讀(Phantom Problem)。在 Next-Key lock演算法下,對索引的掃描,不僅是鎖住掃描到的索引,還有這些索引覆蓋的範圍,因此在這個範圍內插入是不允許的。這樣則避免了另外的事務在這個範圍內的插入導致不可重讀的問題。

6.5.3 丟失更新

丟失更新就是一個事務的更新操作被另一個事務的更新操作覆蓋,從而導致數據不一致。

  1. 事務1將行記錄r更新為v1,但是事務未提交
  2. 事務2將行記錄r更新為v2,事務未提交
  3. 事務1提交
  4. 事務2提交

當前資料庫的任何隔離級別下,以上情況都不會導致資料庫理論意義上的丟失更新問題,因為,對於行的DML操作,需要對行貨其他粗粒度級別的對象加鎖,步驟2,事務2並不能對記錄進行更新,被阻塞,直到事務1提交。

但在生產應用中,還有一個邏輯意義的丟失更新問題,而導致該問題的不是因為資料庫本身的問題,簡單來說,下麵情況會發生丟失更新:

  1. 事務T1查詢一行數據,放入本地記憶體,返回給User1
  2. 事務T2查詢一行數據,放入本地記憶體,返回給User2
  3. User1修改後,更新資料庫提交
  4. User2修改後,更新資料庫提交

顯然,這個過程中,User1的修改操作”丟失“了。在銀行操作中,尤為恐怖。要避免丟失,需要讓事務串列化。

時間 會話A 會話B
1 BEGIN  
2

select cash into @cash

from account

where user=pUser for update

#加X鎖

 
3  

select cash into @cash

from account

where user=pUser for update

#等待,直到m提交後,鎖釋放

  …… ……
m

update account set cash = @cash - 9000

where user = pUser

 
m+1 commit  
m+2  

update account set cash = @cash -1

where user = pUser

m+3   commit

6.6 阻塞

因為不同鎖之間的相容性關係,在有些時刻一個事務中的鎖需要等待另一個事務中的鎖釋放它所占用的資源,這就是阻塞。

阻塞並不是一件壞事,其是為了確保事務可以併發且正常地運行。

需要牢記的是,預設情況下,InnoDB存儲引擎不會回滾超時引發的錯誤異常,其實,InnoDB在大部分情況下都不會對異常進行回滾

時間 會話A 會話B
1

select * from t;

**********3 row *************

a:1

a:2

a:4

 
 2  BEGIN  
 3

 select * from t where a < 4 for update;

**********2 row *************

a:1

a:2

#對(2,4)上X鎖

 
 4    BEGIN
 5    insert into t select 5;
 

 insert into t select 3;

#等待超時,需要確定超時後,5的插入是否需要回滾或提交,否則這是十分危險的狀態

6.7 死鎖

死鎖是指兩個或兩個以上的事務在執行過程中,因爭奪資源而造成的一種相互等待的現象。解決死鎖的辦法:

  1. 超時回滾
  2. wait-for graph 等待圖的方式進行死鎖檢測,採用深度有限的演算法,選擇回滾undo量最小的事務。

死鎖的概率與以下因素有關:

  1. 事務的數量n,數量越多死鎖概率越大
  2. 事務操作的數量r,數量越多,死鎖概率越大
  3. 操作數據的集合R,越小,死鎖概率越大

6.8 鎖升級

鎖升級就是將當前鎖的粒度降低,例如把行鎖升級為頁鎖,把頁鎖升級為表鎖。

InnoDB不存在鎖升級,因為其不是根據每個記錄來產生行鎖的。相反,其根據每個事務訪問的每個頁對鎖進行管理,採用的是點陣圖的方式。因此不管一個事務鎖住頁中的一個記錄還是多個記錄,開銷都是一樣的。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 一.I.MX6 UART驅動 2 文件路徑:\linux_IMX6_CoreC_3.0.35_for_Linux\drivers\tty\serial\imx.c 3 1.驅動入口函數:imx_serial_init() 4 1.1 static int __init imx_serial_in ...
  • #include "ioCC2530.h" #define D3 P1_0#define D4 P1_1#define D5 P1_3#define D6 P1_4#define SW1 P1_2#define SW2 P0_1 /* 簡單的延時函數 */void Delay(unsigned in ...
  • 實驗目的:學會用單片機與電腦之間通過串口通訊。實驗模塊:核心板;實驗內容:由串口調試助手以16進位向單片機發送一數據,如01,如果單片機接收到數據將會原樣返回給電腦,並且顯示在串口調試助手的接收框內。硬體電路圖:在應用單片機的串口和 PC 進行串列通信時,需要進行兩種不同的電平之間的轉換,需要應 ...
  • 工作過程中,代碼在git倉庫中,看代碼工程習慣用本地的VSCode,要在Ubuntu上進行交叉編譯,運行的時候要拽到伺服器里的虛擬機中。 每次改代碼都要從git裡拉最新版,拖拽到Ubuntu里,編譯完之後還要拖拽到伺服器中,步驟麻煩不說,還會遇到各種問題 1、代碼量很大,在Ubuntu里用gedit ...
  • ::當前盤符 @echo current pan : %~d0 ::當前路徑 @echo current path : %cd%\ ::當前bat文件路徑 @echo the bat's path : %~dp0 :: /a表示是個表達式 1M 1024byte 1024 = 1MB set /a ...
  • Hadoop偽分佈安裝搭建 搭建Hadoop的環境 一、準備工作 1、安裝Linux、JDK、關閉防火牆、配置主機名 解壓:tar -zxvf hadoop-2.7.3.tar.gz -C ~/traning/ 設置Hadoop的環境變數: vi ~/.bash_profile HADOOP_HOM ...
  • mysql的inner join等價於where條件連接查詢 內連接 inner join 省略形式 join 外連接 左連接 left outer join 省略形式 left join 右連接 right outer join 省略形式 right join 兩張表內容: mysql> use ...
  • 首先要明白為什麼要用 mysql 的主從複製: 1–在從伺服器可以執行查詢工作 (即我們常說的讀功能),降低主伺服器壓力;(主庫寫,從庫讀,降壓) 2–在從主伺服器進行備份,避免備份期間影響主伺服器服務;(確保數據安全) 3–當主伺服器出現問題時,可以切換到從伺服器。(提升性能) 來說一下主從複製的 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...