1. 程式人生 > >C#執行緒--5.0之前時代(二)、執行緒的同步

C#執行緒--5.0之前時代(二)、執行緒的同步

執行緒同步

說明:接上一篇,注意分享執行緒同步的必要性和執行緒同步的方法。

一、什麼是執行緒同步:

在同一時間只允許一個執行緒訪問資源的情況稱為執行緒同步。

二、為什麼需要執行緒同步:

  • 避免競爭條件;
  • 確保執行緒安全;(如果兩個執行緒同時訪問一個資源並對那個資源做修改,就不安全了)

現在的計算機變得越來越多核,每一個CPU可以獨立工作,但是對於記憶體和外部資源、資料庫的訪問卻可能因為不同執行緒的訪問使資料產生異常,常見的例子就是銀行的轉賬的例子不再贅述。

 

三、執行緒同步的方法:

  • 同步程式碼中重要的部分;
  • 使物件不可改變;
  • 使用執行緒安全包裝器;

    注意:區域性變數、方法引數和返回值是放在堆疊中的,本身是執行緒安全的。

四、執行緒不安全的演示:

背景:在資料庫的user_blance表插入兩條資料,兩人的balance值都為1000.00,整個user_balance表的balance總值為2000.00

        static string connectionStr = "Server=127.0.0.1;Port=3306;Stmt=;Database=exe_dev; User=root;Password=123456";
        public static void UnSafeThread() {
            Thread ThreadOne = new Thread(new
ThreadStart(DrawMoney)); ThreadOne.Name = "A001"; Thread ThreadTwo = new Thread(new ThreadStart(DrawMoney)); ThreadTwo.Name = "A002"; ThreadOne.Start(); ThreadTwo.Start(); } private static void DoDrawMoney() { Random random
= new Random(); int money = random.Next(100); string userId = Thread.CurrentThread.Name; string selectSql = "select balance from user_balance where [email protected]"; string updateSql = "update user_balance set [email protected][email protected] where [email protected]"; string updateSql2 = "update user_balance set [email protected]@Money where user_id<>@UserId"; using (MySqlConnection conn= MySqlConnectionHelper.OpenConnection(connectionStr)) { var balance = conn.ExecuteScalar(selectSql, new { UserId = userId }); if (balance != null) { conn.Execute(updateSql, new { Money = money, Balance=balance, UserId = userId }); conn.Execute(updateSql2, new { Money = money, Balance = balance, UserId = userId }); } } } private static void DrawMoney() { for (int i = 0; i < 100; i++) { DoDrawMoney(); } }

執行結果:

程式中有三條執行緒在跑:兩條支執行緒,一條主執行緒,主執行緒負責統計錢的總數,兩條支執行緒模擬兩個人賺錢,賺過來賺過去,哈哈哈,依據查詢成果可以看到,錢的總數原本是2000.00,但是之後開始減少。當然上面的異常也可以通過加事務解決,或者改變sql的實現方式balance=balance+money,不過這個不是我們討論的重點,不展開。

 五、執行緒同步:
1、MethodImplAttribute:同步方法

  MethodImplAttribute是一個屬性,它用來告訴CLR方法是如何實現的,MethodImplAttribute的一個建構函式把MethodImplOptions的列舉值作為引數,MethodImplOptions的列舉值Synchronized告訴CLR,這個方法該一次性只能在一個執行緒上執行。 靜態方法在型別上鎖定,而例項方法在例項上鎖定。 只有一個執行緒可在任意例項函式中執行,且只有一個執行緒可在任意類的靜態函式中執行。

使用方式:在需要同步的方法上新增屬性

[MethodImpl(MethodImplOptions.Synchronized)]
        [MethodImpl(MethodImplOptions.Synchronized)]
        private static void DoDrawMoney()
        {
            Random random = new Random();
            int money = random.Next(100);

            string userId = Thread.CurrentThread.Name;
            string selectSql = "select balance from user_balance where [email protected]";
            string updateSql = "update user_balance set [email protected][email protected] where [email protected]";
            string updateSql2 = "update user_balance set [email protected] where user_id<>@UserId";
            using (MySqlConnection conn= MySqlConnectionHelper.OpenConnection(connectionStr))
            {
                var balance = conn.ExecuteScalar(selectSql, new { UserId = userId });
                if (balance != null)
                {
                    conn.Execute(updateSql, new { Money = money, Balance=balance, UserId = userId });
                    conn.Execute(updateSql2, new { Money = money, Balance = balance, UserId = userId });
                }
            }
        }

 

 

 

 

持續更新中...