在多個線程之間共享數據時,需要考慮線程同步問題,必須確保每次只有一個線程訪問和改變共享數據。
C#中使用lock語句可以輕松地設置和解除鎖定以期達到每次只有一個線程訪問和改變共享數據的目的。
下面是一個多線程訪問共享數據的實例,看看在沒有進行同步操作的情況下會出現什麼樣的問題?
using System; using System.Threading; namespace LockExamples { class Program { static int account = 1000;//賬戶 static int pocket = 0;//口袋 static void Main(string[] args) { int threadCount = 10; var threads = new Thread[threadCount]; for (int i = 0; i < threadCount; i++) { threads[i] = new Thread(DoWork); threads[i].Start(); } for (int i = 0; i < threadCount; i++) { threads[i].Join(); } Console.WriteLine("pocket=" + pocket); } public static void DoWork() { if (account >= 1000) { Thread.Sleep(10);//自動取款機打了個小盹 account -= 1000; pocket += 1000; } } } }
可以將示例代碼理解成:一個用戶分十次從自己的銀行賬戶中取錢。
取錢的邏輯由下面的代碼來實現。
if (account >= 1000) { Thread.Sleep(10);//自動取款機打了個小盹 account -= 1000; pocket += 1000; }
當賬戶中的余額大於等於1000時,就取出1000放進自己的口袋。
因為用戶當前的賬戶中僅剩下1000,所以就算用戶取了10次,最終口袋中也應該只有1000。那麼實際情況又是怎樣的呢?
請看下面的執行結果(結果也可能是1000,2000,...,9000中的一個)。
結果竟然是10000!!!!!!!!
用戶從僅有1000余額的賬戶中取出了10000,實在是一件令人振奮人心的事。
不過對於銀行來說,這可不是件什麼好事,因為照這樣下去,銀行的錢遲早會被用戶掏空。<喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+zqrKssO0u+Gz9s/W1eLR+bXEveG5+8TYo788L3A+CjxwPtXiysfS8s6qw7vT0LbUtuDP37PMt8POyrmyz+3K/b7dvfjQ0M2ssr2jrDEwuPbP37PMzazKsb34yOvBy8ihx661xMLfvK2jrMv50tTSu7myyKGz9sHLMTAwMDChozwvcD4KPHA+zqrBy73ivvbV4rj2zsrM4qOsv8nS1Mq508Nsb2Nr0+++5MC0zayyvbbgz9+zzLfDzsq5ss/tyv2+3aGjz8LD5srH1Pa802xvY2vT777kuvO1xMihx67C37ytoaM8L3A+CjxwPjwvcD4KPHByZSBjbGFzcz0="brush:java;">private static object o = new object(); lock (o) { if (account >= 1000) { Thread.Sleep(10);//自動取款機打了個小盹 account -= 1000; pocket += 1000; } }代碼中使用lock關鍵字鎖定對象o,當一個線程獲得鎖定後,其他線程就無法再獲得鎖定,只有當當前線程解除鎖定後,其他對象才可以重新獲得鎖定,這樣一來,就可以保證每次只有一個線程獲得鎖定進而訪問和修改共享數據。
多次執行修改後的示例代碼,每次都可以得到以下的正確結果。
lock語句鎖定的對象,必須是引用類型。因為鎖定值類型只是鎖定了一個副本,沒什麼意義。