2012-07-24 84 views
1

與交易玩的第一次,我想我會得到下面的代碼工作:這個交易範圍爲什麼不起作用?

namespace database 
{ 
    class Program 
    { 
     static string connString = "Server=ServerName;Database=Demo;Trusted_Connection=True;"; 
     SqlConnection connection = new SqlConnection(connString); 
     static Random r = new Random(); 


     static void Add() 
     { 
      try 
      { 
       Thread.Sleep(r.Next(0, 10)); 
       using (var trans = new TransactionScope()) 
       { 
        using (var conn = new SqlConnection(connString)) 
        { 
         conn.Open(); 

         var count = (int)new SqlCommand("select balance from bank WITH (UPDLOCK) where owner like '%Jan%'", conn).ExecuteScalar(); 
         Thread.Sleep(r.Next(0, 10)); 
         SqlCommand cmd = new SqlCommand("update bank set balance = " + ++count + "where owner like '%Jan%'", conn); 
         cmd.ExecuteNonQuery(); 
        } 
        trans.Complete(); 
       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.Message); 
      } 
     } 

     static void Remove() 
     { 
      try 
      { 
       Thread.Sleep(r.Next(0, 10)); 
       using (var trans = new TransactionScope()) 
       { 
        using (var conn = new SqlConnection(connString)) 
        { 
         conn.Open(); 

         var count = (int)new SqlCommand("select balance from bank WITH (UPDLOCK) where owner like '%Jan%'", conn).ExecuteScalar(); 
         Thread.Sleep(r.Next(0, 10)); 
         SqlCommand cmd = new SqlCommand("update bank set balance = " + --count + "where owner like '%Jan%'", conn); 
         cmd.ExecuteNonQuery(); 

        } 
        trans.Complete(); 
       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.Message); 
      } 
     } 


     static void Main(string[] args) 
     { 
      for (int i = 0; i < 5; i++) 
      { 
       Thread t = new Thread(new ThreadStart(Add)); 
       t.Start(); 
      } 
      for (int i = 0; i < 5; i++) 
      { 
       Thread t = new Thread(new ThreadStart(Remove)); 
       t.Start(); 
      } 
      Console.ReadLine(); 
     } 
    } 
} 

我認爲在年底增加了100和100後,減法我的平衡到達是同我的出發點 - 100,但是每次運行腳本時它都會不斷變化。即使有隔離級別的序列化。有誰能告訴我爲什麼? O_o

編輯:將連接打開和關閉移動到事務處理範圍內。現在 的問題是,我得到


像馬克Gravell說「事務(進程ID XX)已被死鎖的鎖資源與另一個進程,並已被選作死鎖犧牲品重新運行該事務。」: 把事務範圍內的連接,並添加UPDLOCK的選擇查詢與變更的IsolationLevel到REPEATABLEREAD結合的伎倆:)

 static void Add() 
     { 
      try 
      { 
       Thread.Sleep(r.Next(0, 10)); 
       using (var trans = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.RepeatableRead })) 
       { 
        using (var conn = new SqlConnection(connString)) 
        { 
         conn.Open(); 

         var count = (int)new SqlCommand("select balance from bank WITH (UPDLOCK) where owner like '%Jan%'", conn).ExecuteScalar(); 
         Thread.Sleep(r.Next(0, 10)); 
         SqlCommand cmd = new SqlCommand("update bank set balance = " + ++count + "where owner like '%Jan%'", conn); 
         cmd.ExecuteNonQuery(); 
        } 
        trans.Complete(); 
       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.Message); 
      } 
     } 
+0

「但是每次運行腳本時它都會不斷變化」 - 請澄清一下:你在看哪裏,以及你看到的是什麼**。它*最終*相同嗎?問題是你看到的中間值?要麼...? – 2012-07-24 08:54:46

+0

我通過Microsoft SQL Server Management Studio查看我的數據庫。我手動將平衡值設置爲100(int),運行一次程序,然後按F5查看通常類似於92-97的結果 – user1384085 2012-07-24 09:00:08

+0

@ user1385085是在**所有操作之後的**嗎?和:有沒有例外? – 2012-07-24 09:00:50

回答

6

1:目前TransactionScope可能是多餘的和未使用的;嘗試改變交易包住連接,而不是周圍的其他方式(哦,並使用using):

using (var trans = new TransactionScope(TransactionScopeOption.Required, 
     new TransactionOptions() { IsolationLevel = IsolationLevel.Serializable })) 
using (var conn = new SqlConnection(connString)) 
{ 
    conn.Open(); 
    //... 
    trans.Complete(); 
} 

這樣,連接應該正確地爭取的事務中(如果事情妥善清理不好的事情發生)

我認爲上述的主要問題是;即不參與交易。這意味着可能會丟失更改,因爲讀寫操作實際上並未提升到更高的隔離級別。

2:但是,如果你這樣做本身,我想你會看到死鎖。爲了避免死鎖,如果你知道你要更新,你可能想上使用(UPDLOCK)select - 這需要一個寫鎖在一開始,所以,如果有一個競爭的線程你會得到一個塊,而不是僵局。

  • 線程A讀取行,得到一個讀鎖
  • 線程B讀取行,得到一個讀鎖
  • 線程A嘗試:

    需要明確的是,這種僵局的情況是造成更新的行,並且由乙阻塞

  • 線程B試圖更新該行,並且由

添加阻塞,這變爲:

  • 線程A讀取該行,得到一個寫鎖定
  • 線程B試圖讀取該行,並且由
  • 線程A阻塞更新該行
  • 線程A完成交易
  • 線程B能夠繼續,讀取該行,得到一個寫鎖
  • 線程B更新該行
  • 線程B完成了反式行動

3:但查詢做一個微不足道的更新是愚蠢的;更好的方式是在不選擇的情況下發布就地更新,即update bank set balance = balance + 1 where ...

+0

事實上,在事務範圍內移動Connection打開和關閉會將我的問題更改爲鎖定問題。然而,添加UPDLOCK似乎並沒有解決這個問題: – user1384085 2012-07-24 10:02:03

+0

哦,做這件事的原因只是爲了試驗交易。實際上,餘額=餘額+ 1會在這裏實現這個技巧 – user1384085 2012-07-24 10:03:51

+0

@ user1384085它*應該*修復這個問題;你能證明你有什麼?注意:阻止**不是**問題,並且是預期的。它是*死鎖*,這是一個問題。 – 2012-07-24 10:06:15

3

您必須在TransactionScope塊內打開連接

而不是

var conn = new SqlConnection(connString); 
conn.Open(); 
using (var trans = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.Serializable })) 
{ 
    // do stuff 
} 

使用這樣

using (var trans = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.Serializable })) 
using (var conn = new SqlConnection(connString)) 
{ 
    conn.Open(); 
    // do stuff 
} 

這樣打開連接自動登記它在TransactionScope作爲一個輕量級的事務。您可以隨時查看examples

+0

謝謝。是的,這似乎是第一個問題。這樣做,我得到鎖問題。 – user1384085 2012-07-24 10:05:06