2018-11-07日記
概覽
今日立冬, 資訊時代帶來的焦躁讓學習無法深入, 所以打算以寫日記的形式戒掉焦躁, 重拾醉心學習的狀態.
- Synchronized與SyncRoot技術同步執行緒資料
- Serializable特性作用
- RPC(遠端方法呼叫)
資料同步
在多個執行緒中共享資料, 很容易出現
爭用條件
和死鎖
爭用條件(RaceCondition)
設想如下程式碼和步驟:
- 第一個執行緒程式執行到
if
語句, 假設此時_state
等於5, 條件為真 - 進入
if
語句之後, 它就被其他執行緒搶佔, 排程器執行另一個執行緒 - 在另一個執行緒中某些程式碼將
_state
的值改變為6
- 第一個執行緒再次被排程, 此時
_state
等於6
, 在執行完_state++
語句後,_state
將等於7 - 提示: 實際上
_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集合型別中, 比如
Hashtable
和ArrayList
都有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 今天太晚, 沒時間再寫了, 以後補上.
延申閱讀
- 併發集合型別