日記(2018-11-07)

學友2000發表於2018-11-07

2018-11-07日記

概覽

今日立冬, 資訊時代帶來的焦躁讓學習無法深入, 所以打算以寫日記的形式戒掉焦躁, 重拾醉心學習的狀態.

  • Synchronized與SyncRoot技術同步執行緒資料
  • Serializable特性作用
  • RPC(遠端方法呼叫)

資料同步

在多個執行緒中共享資料, 很容易出現爭用條件死鎖

爭用條件(RaceCondition)

設想如下程式碼和步驟:

  1. 第一個執行緒程式執行到if語句, 假設此時_state等於5, 條件為真
  2. 進入if語句之後, 它就被其他執行緒搶佔, 排程器執行另一個執行緒
  3. 在另一個執行緒中某些程式碼將_state的值改變為6
  4. 第一個執行緒再次被排程, 此時_state等於6, 在執行完_state++語句後, _state將等於7
  5. 提示: 實際上 _state++的操作將從記憶體中獲取值,給該值增加1, 然後再寫回記憶體. 這些操作都可能被執行緒排程器打斷, 造成執行緒不安全!
public void ChangeState()
{
    if (_state == 5)
    {
        _state++;
    }
}

解決方法是給_state物件加鎖, 將鎖定物件設定為執行緒安全物件, 一個執行緒鎖住了_state物件, 其他執行緒就必須等待該鎖定解除.

但是_state值物件, 不是引用物件, lock只能鎖住引用物件, 因為鎖住一個值的副本毫無意義. 那麼就需要用一個物件來同步.程式碼如下:

public class StateObject
{
    private int _state = 5;
    private object sync = new object();
    
    public void ChangeState()
    {
        lock (sync)
        {
            if (_state == 5)
            {
                _state++;
            }
        }

    }
}

死鎖(Deadlock)

過多的鎖會導致執行緒都再等待對方解除鎖定, 出現死鎖. 所以再程式設計一開始就需要考慮到死鎖問題, 設計好鎖定順序和鎖定超時時間.

Synchronized與SyncRoot技術

再.net集合型別中, 比如HashtableArrayList都有Synchronized靜態方法和SyncRoot實力方法. 返回一個執行緒安全的SyncHashtable物件, 這個物件中的方法, 比如Add都會鎖定SyncRoot以確保執行緒安全的操作集合.

 namespace SynchronizationSamples
 {
     public class Demo
     {
         public virtual bool IsSynchronized => false;
         public virtual void DoThis() {}
         public virtual void DoThat() {}

         public static Demo Synchronized(Demo d)
         {
             if (!d.IsSynchronized)
             {
                 return new Synchronized(d);
             }
             return d;
         }
     }  
     
    // 同步版本
    private class SynchronizedDemo: Demo
    {
        public override bool IsSynchronized => true;
        private object _syncRoot = new object();
        private Demo _d;

        public SynchronizedDemo(Demo d)
        {
            _d = d;
        }

        public override void DoThis() 
        {
            lock (_syncRoot)
            {
                _d.DoThis();
            }
        }
        public override void DoThat() 
        {
            lock (_syncRoot)
            {
                _d.DoThat();
            }
        }

    }
     
 }

SyncRoot

確保再一個例項中, 不管再程式碼的任何位置呼叫, 返回的都是同一個物件, 它是唯一的.

public virtual object SyncRoot
{
    get
    {
        if (this._syncRoot == null)
        {
            // Interlocked為多個執行緒共享的變數提供原子操作, 原子操作就是單執行緒操作
            Interlocked.CompareExchange(ref this._syncRoot, new object(), null);
        }
        return this._syncRoot;
    }
}

Serializable特性和RPC

todo: #1 今天太晚, 沒時間再寫了, 以後補上.

延申閱讀

  • 併發集合型別

腳註