整理一下lock的特性。
1.lock的目標並不是物件,而是程式碼區段,只有被lock包覆的程式區段才會有作用。
2.當使用lock(xxx)時,xxx可以想象成是一個識別號,所有相同識別號的lock程式區段,會受到lock的影響。
3.lock並不能使用struct來當作識別號。
4.在大家都在存取"相同記憶體位置時" lock 這個標籤才有使用的意義.
from:http://www.dotblogs.com.tw/fphoenix/archive/2009/05/25/8557.aspx
MSDN的提示:
提供給 lock 關鍵字的引數必須是依據參考型別的物件,且用來定義鎖定的範圍。 在上述範例中,鎖定範圍是限制為此函式,因為在函式外部不存在物件 lockThis 的參考。 如果這類參考並不存在,鎖定範圍便會擴充至該物件。 嚴格說來,提供的物件僅用來唯一識別正由多執行緒共用的資源,所以可能是任意的類別執行個體。 不過實際上,此物件通常代表執行緒同步處理所需要的資源。 例如,如果容器物件是由多執行緒使用,則該容器可以傳遞至鎖定,在鎖定之後的同步化程式碼區塊將可存取容器。 只要其他執行緒在存取容器前鎖定了同一個容器,就可以安全地同步處理對物件的存取。
一般而言,您最好避免鎖定 public 型別,或避免鎖定不在應用程式控制範圍內的物件執行個體。 例如,若執行個體可以公開地被存取,lock(this) 可能會產生問題,因為在您控制範圍之外的程式碼可能也會鎖定物件。 這樣可能會建立死結狀況,在此狀況下有兩個或以上的執行緒等候同一個物件的釋放。 相對於物件,鎖定公用資料型別會造成相同原因的問題。 而鎖定常值字串則特別具風險性,因為常值字串是由 Common Language Runtime (CLR) 所「拘留」(interned)。 這表示對於整個程式,任何指定之字串常值有一個執行個體,完全相同的物件代表在所有執行緒上、所有執行的應用程式定義域中的常值。 因此,不論在應用程式處理序的任何地方,置於相同內容字串上的鎖定將會鎖定應用程式中該字串的所有執行個體。 因此,最好鎖定未被拘留的 private 或 protected 成員。 某些類別特別為鎖定提供成員。 例如,Array 型別提供了 SyncRoot。 許多集合型別也提供 SyncRoot 成員。
Object thisLock = new Object();
lock (thisLock)
{
// Critical code section.
}
如需詳細資訊,請參閱執行緒同步處理 (C# 和 Visual Basic)。
備註
lock 關鍵字可保證有執行緒在關鍵區段時,絕對不會有任何其他執行緒同時也進入這個關鍵區段執行。如果其他執行緒嘗試進入已鎖定的程式碼,它將會等候、封鎖,直到該物件被釋出。
執行緒 (C# 和 Visual Basic) 這一章節會討論執行緒的處理。
lock 關鍵字會在區塊開始執行時呼叫 Enter,並在區塊結束時呼叫 Exit。
一般而言,請避免鎖定 public 型別或程式碼無法控制的執行個體。有三種常見的建構,分別為 lock (this)、lock (typeof (MyType)) 和 lock ("myLock"),違反這項方針:
lock (this) 在可公開存取執行個體的情況下,會是問題所在。
lock (typeof (MyType)) 在可公開存取 MyType 的情況下,會是問題所在。
lock(“myLock”) 會是問題所在,因為使用相同字串的處理序中若有任何其他程式碼,將會共用相同的鎖定。
最佳作法是定義要鎖定的 private 物件,或者定義 private static 物件變數保護所有執行個體通用的資料。
範例
以下的範例顯示在 C# 中執行緒 (但不用鎖定) 的簡易用法。
C#
//using System.Threading;
class ThreadTest
{
public void RunMe()
{
Console.WriteLine("RunMe called");
}
static void Main()
{
ThreadTest b = new ThreadTest();
Thread t = new Thread(b.RunMe);
t.Start();
}
}
// Output: RunMe called
下列範例使用到執行緒與 lock。只要 lock 陳述式存在,此陳述式區塊就是關鍵區段,而且 balance 永遠不會成為負數。
C#
// using System.Threading;
class Account
{
private Object thisLock = new Object();
int balance;
Random r = new Random();
public Account(int initial)
{
balance = initial;
}
int Withdraw(int amount)
{
// This condition will never be true unless the lock statement
// is commented out:
if (balance < 0)
{
throw new Exception("Negative Balance");
}
// Comment out the next line to see the effect of leaving out
// the lock keyword:
lock (thisLock)
{
if (balance >= amount)
{
Console.WriteLine("Balance before Withdrawal : " + balance);
Console.WriteLine("Amount to Withdraw : -" + amount);
balance = balance - amount;
Console.WriteLine("Balance after Withdrawal : " + balance);
return amount;
}
else
{
return 0; // transaction rejected
}
}
}
public void DoTransactions()
{
for (int i = 0; i < 100; i++)
{
Withdraw(r.Next(1, 100));
}
}
}
class Test
{
static void Main()
{
Thread[] threads = new Thread[10];
Account acc = new Account(1000);
for (int i = 0; i < 10; i++)
{
Thread t = new Thread(new ThreadStart(acc.DoTransactions));
threads[i] = t;
}
for (int i = 0; i < 10; i++)
{
threads[i].Start();
}
}
}
留言列表