並發可能產生的三種問題(測試代碼)
臟讀
定義:A事務執行過程中B事務讀取了A事務的修改,但是A事務並沒有結束(提交),A事務后來可能成功也可能失敗。
比喻:A修改了源代碼並且並沒有提交到源代碼系統,A直接通過QQ將代碼發給了B,A后來取消了修改。
代碼示例
1 [TestMethod] 2 public void 臟讀_測試() 3 { 4 //前置條件 5 using (var context = new TestEntities()) 6 { 7 Assert.AreEqual(1, context.Tables.Count()); 8 } 9 10 var autoResetEvent = new AutoResetEvent(false); 11 12 var transactionOptions1 = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }; 13 var transactionOptions2 = new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted }; 14 15 using (var ts1 = new TransactionScope(TransactionScopeOption.Required, transactionOptions1)) 16 { 17 //添加數據 18 using (var context = new TestEntities()) 19 { 20 context.Tables.Add(new Table() { Id = Guid.NewGuid(), Name = "段光偉" }); 21 context.SaveChanges(); 22 } 23 24 ThreadPool.QueueUserWorkItem(data => 25 { 26 using (var ts2 = new TransactionScope(TransactionScopeOption.Required, transactionOptions2)) 27 { 28 //臟讀測試 29 using (var context = new TestEntities()) 30 { 31 Assert.AreEqual(2, context.Tables.Count()); 32 } 33 } 34 35 autoResetEvent.Set(); 36 }); 37 38 autoResetEvent.WaitOne(); 39 } 40 41 //后置條件 42 using (var context = new TestEntities()) 43 { 44 Assert.AreEqual(1, context.Tables.Count()); 45 } 46 }
不可重復讀
定義:A事務讀取了兩次數據,在這兩次的讀取過程中B事務修改了數據,A事務的這兩次讀取出來的數據不一樣了(不可重復讀)。
比喻:A在做源代碼審查,在審查的過程中獲取了兩次源代碼,在這兩次獲取期間B修改了源代碼,B修改的很可能是A審查過的代碼,而這部分代碼可能不符合規范了。
代碼示例
1 [TestMethod] 2 public void 不可重復讀_測試() 3 { 4 var autoResetEvent = new AutoResetEvent(false); 5 6 var transactionOptions1 = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }; 7 var transactionOptions2 = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }; 8 9 using (var ts1 = new TransactionScope(TransactionScopeOption.Required, transactionOptions1)) 10 { 11 //前置條件 12 using (var context = new TestEntities()) 13 { 14 Assert.AreEqual("李妞妞", context.Tables.First().Name); 15 } 16 17 ThreadPool.QueueUserWorkItem(data => 18 { 19 using (var ts2 = new TransactionScope(TransactionScopeOption.Required, transactionOptions2)) 20 { 21 //修改數據 22 using (var context = new TestEntities()) 23 { 24 context.Tables.First().Name = "段光偉"; 25 context.SaveChanges(); 26 } 27 28 ts2.Complete(); 29 } 30 31 autoResetEvent.Set(); 32 }); 33 34 autoResetEvent.WaitOne(); 35 36 //不可重復讀測試 37 using (var context = new TestEntities()) 38 { 39 Assert.AreEqual("段光偉", context.Tables.First().Name); 40 } 41 } 42 }
幻讀
定義:A事務讀取了兩次數據,在這兩次的讀取過程中B事務添加了數據,A事務的這兩次讀取出來的集合不一樣了(幻讀)。
比喻:A在統計文件數據,為了統計精確A統計了兩次,在這兩次的統計過程中B添加了一個文件,A發現這兩次統計的數量不一樣(幻讀),A會感覺自己的腦袋有點頭疼。
代碼示例
1 [TestMethod] 2 public void 幻讀_測試() 3 { 4 var autoResetEvent = new AutoResetEvent(false); 5 6 var transactionOptions1 = new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead }; 7 var transactionOptions2 = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }; 8 9 using (var ts1 = new TransactionScope(TransactionScopeOption.Required, transactionOptions1)) 10 { 11 //前置條件 12 using (var context = new TestEntities()) 13 { 14 Assert.AreEqual(1, context.Tables.Count()); 15 } 16 17 ThreadPool.QueueUserWorkItem(data => 18 { 19 using (var ts2 = new TransactionScope(TransactionScopeOption.Required, transactionOptions2)) 20 { 21 //添加數據 22 using (var context = new TestEntities()) 23 { 24 context.Tables.Add(new Table() { Id = Guid.NewGuid(), Name = "段光偉" }); 25 context.SaveChanges(); 26 } 27 28 ts2.Complete(); 29 } 30 31 autoResetEvent.Set(); 32 }); 33 34 autoResetEvent.WaitOne(); 35 36 //幻讀測試 37 using (var context = new TestEntities()) 38 { 39 Assert.AreEqual(2, context.Tables.Count()); 40 } 41 } 42 }
四種隔離級別如何處理並發問題
臟讀 | 不可重復讀 | 幻讀 | |
讀未提交 | 允許 | 允許 | 允許 |
讀已提交 | 不允許 | 允許 | 允許 |
可重復讀 | 不允許 | 不允許 | 允許 |
串行化 | 不允許 | 不允許 | 不允許 |