C#多線程之線程同步_第1頁
C#多線程之線程同步_第2頁
C#多線程之線程同步_第3頁
C#多線程之線程同步_第4頁
C#多線程之線程同步_第5頁
已閱讀5頁,還剩9頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

第C#多線程之線程同步我們猜想一下程序的輸出結(jié)果是多少?2000?我們運行程序看一下輸出結(jié)果:

我們看到,程序最后輸出的結(jié)果跟我們預(yù)測的完全不一樣,這是什么原因呢?這就是由線程同步引起的問題。

線程同步問題:是解決多個線程同時操作一個資源的問題。

在上面的例子中,t1和t2兩個線程里面都是讓變量Counter的值自增1,假設(shè)這時t1線程讀取到Counter的值為200,可能t2線程執(zhí)行非常快,t1線程讀取Counter值的時候,t2線程已經(jīng)把Counter的值改為了205,等t1線程執(zhí)行完畢以后,Counter的值又被變?yōu)榱?01,這樣就會出現(xiàn)線程同步的問題了。那么該如何解決這個問題呢?

二、解決線程同步問題

1、lock

解決線程同步問題最簡單的是使用lock。lock可以解決多個線程同時操作一個資源引起的問題。lock是C#中的關(guān)鍵字,它要鎖定一個資源,lock的特點是:同一時刻只能有一個線程進入lock的對象的范圍,其它lock的線程都要等待。我們看下面優(yōu)化后的代碼:

usingSystem;

usingSystem.Threading;

namespaceThreadSynchDemo

classProgram

privatestaticintCounter=0;

//定義一個locker對象

privatestaticObjectlocker=newObject();

staticvoidMain(string[]args)

#region存在線程同步問題

//Threadt1=newThread(()={

//for(inti=0;i1000;i++)

//{

//Counter++;

//Thread.Sleep(1);

//}

//});

//t1.Start();

//Threadt2=newThread(()={

//for(inti=0;i1000;i++)

//{

//Counter++;

//Thread.Sleep(1);

//}

//});

//t2.Start();

#endregion

#region使用Lock解決線程同步問題

Threadt1=newThread(()={

for(inti=0;i1000;i++)

lock(locker)

Counter++;

Thread.Sleep(1);

t1.Start();

Threadt2=newThread(()={

for(inti=0;i1000;i++)

lock(locker)

Counter++;

Thread.Sleep(1);

t2.Start();

#endregion

Thread.Sleep(3000);

Console.WriteLine(Counter);

Console.ReadKey();

}

這時我們在運行程序,查看輸出結(jié)果:

這時輸出結(jié)果是正確的。

注意:lock只能鎖住同一個對象,如果是不同的對象,還是會有線程同步的問題。lock鎖定的對象必須是引用類型的對象。

我們在定義一個Object類型的對象,lock分別鎖住兩個對象,看看是什么結(jié)果:

usingSystem;

usingSystem.Threading;

namespaceThreadSynchDemo

classProgram

privatestaticintCounter=0;

//定義一個locker對象

privatestaticObjectlocker=newObject();

//定義locker2

privatestaticObjectlocker2=newObject();

staticvoidMain(string[]args)

#region存在線程同步問題

//Threadt1=newThread(()={

//for(inti=0;i1000;i++)

//{

//Counter++;

//Thread.Sleep(1);

//}

//});

//t1.Start();

//Threadt2=newThread(()={

//for(inti=0;i1000;i++)

//{

//Counter++;

//Thread.Sleep(1);

//}

//});

//t2.Start();

#endregion

#region使用Lock解決線程同步問題

//Threadt1=newThread(()={

//for(inti=0;i1000;i++)

//{

//lock(locker)

//{

//Counter++;

//}

//Thread.Sleep(1);

//}

//});

//t1.Start();

//Threadt2=newThread(()={

//for(inti=0;i1000;i++)

//{

//lock(locker)

//{

//Counter++;

//}

//Thread.Sleep(1);

//}

//});

//t2.Start();

#endregion

#region使用lock鎖住不同的對象也會有線程同步問題

Threadt1=newThread(()={

for(inti=0;i1000;i++)

lock(locker)

Counter++;

Thread.Sleep(1);

t1.Start();

Threadt2=newThread(()={

for(inti=0;i1000;i++)

lock(locker2)

Counter++;

Thread.Sleep(1);

t2.Start();

#endregion

Thread.Sleep(3000);

Console.WriteLine(Counter);

Console.ReadKey();

}

程序運行結(jié)果:

可以看到,這時還是會有線程同步的問題。雖然使用了lock,但是我們鎖住的是不同的對象,這樣也會有線程同步問題。lock必須鎖住同一個對象才可以。

我們下面在來看一個多線程同步問題的例子:

usingSystem;

usingSystem.Threading;

namespaceThreadSynchDemo2

classProgram

staticintMoney=100;

///summary

///定義一個取錢的方法

////summary

///paramname="name"/param

staticvoidQuQian(stringname)

Console.WriteLine(name+"查看一下余額"+Money);

intyue=Money-1;

Console.WriteLine(name+"取錢");

Money=yue;

Console.WriteLine(name+"取完了,剩"+Money);

staticvoidMain(string[]args)

Threadt1=newThread(()={

for(inti=0;ii++)

QuQian("t2");

Threadt2=newThread(()={

for(inti=0;ii++)

QuQian("t2");

t1.Start();

t2.Start();

t1.Join();

t2.Join();

Console.WriteLine("余額"+Money);

Console.ReadKey();

}

我們看一下輸出結(jié)果:

可以看到,最終的余額并不是80,這也是線程同步帶來的問題,如何解決。解決思路就是使用同步的技術(shù)避免兩個線程同時修改一個余額。

2、最大粒度同步方法

在方法上面使用[MethodImpl(MethodImplOptions.Synchronized)],標(biāo)記該方法是同步方法,這樣一個方法只能同時被一個線程訪問。我們在QuQian的方法上面標(biāo)記,修改后的代碼如下:

usingSystem;

usingSystem.Runtime.CompilerServices;

usingSystem.Threading;

namespaceThreadSynchDemo2

classProgram

staticintMoney=100;

///summary

///定義一個取錢的方法,在上面標(biāo)記為同步方法

////summary

///paramname="name"/param

[MethodImpl(MethodImplOptions.Synchronized)]

staticvoidQuQian(stringname)

Console.WriteLine(name+"查看一下余額"+Money);

intyue=Money-1;

Console.WriteLine(name+"取錢");

Money=yue;

Console.WriteLine(name+"取完了,剩"+Money);

staticvoidMain(string[]args)

Threadt1=newThread(()={

for(inti=0;ii++)

QuQian("t2");

Threadt2=newThread(()={

for(inti=0;ii++)

QuQian("t2");

t1.Start();

t2.Start();

t1.Join();

t2.Join();

Console.WriteLine("余額"+Money);

Console.ReadKey();

}

程序輸出結(jié)果:

現(xiàn)在的方法就是線程安全的了。什么是線程安全呢?線程安全是指方法可以被多個線程隨意調(diào)用,而不會出現(xiàn)混亂。如果出現(xiàn)了混亂,那么就是線程不安全的。線程安全的方法可以在多線程里面隨意的使用。

3、對象互斥鎖

對象互斥鎖就是我們上面講的lock。我們在用lock來修改上面QuQian的例子:

usingSystem;

usingSystem.Runtime.CompilerServices;

usingSystem.Threading;

namespaceThreadSynchDemo2

classProgram

staticintMoney=100;

///summary

///定義一個取錢的方法,在上面標(biāo)記為同步方法

////summary

///paramname="name"/param

//[MethodImpl(MethodImplOptions.Synchronized)]

//staticvoidQuQian(stringname)

//Console.WriteLine(name+"查看一下余額"+Money);

//intyue=Money-1;

//Console.WriteLine(name+"取錢");

//Money=yue;

//Console.WriteLine(name+"取完了,剩"+Money);

privatestaticobjectlocker=newobject();

staticvoidQuQian(stringname)

Console.WriteLine(name+"查看一下余額"+Money);

intyue=Money-1;

Console.WriteLine(name+"取錢");

Money=yue;

Console.WriteLine(name+"取完了,剩"+Money);

staticvoidMain(string[]args)

Threadt1=newThread(()={

for(inti=0;ii++)

//使用對象互斥鎖

lock(locker)

QuQian("t1");

Threadt2=newThread(()={

for(inti=0;ii++)

lock(locker)

QuQian("t2");

t1.Start();

t2.Start();

t1.Join();

t2.Join();

Console.WriteLine("余額"+Money);

Console.ReadKey();

}

程序輸出結(jié)果:

可以看到,最終的輸出結(jié)果還是80。

同一時刻只能有一個線程進入同一個對象的lock代碼塊。必須是同一個對象才能起到互斥的作用。lock后必須是引用類型,不一定是object,只要是對象就行。

鎖對象選擇很重要,選不對就起不到同步的作用;選不對還有可能會造成其他地方被鎖,比如用字符串做鎖(因為字符串緩沖池導(dǎo)致導(dǎo)致可能用的是其他地方正在使用的鎖),所以不建議使用字符串做鎖。下面的代碼就是不允許的:

lock("locker")

兩個方法如果都用一個對象做鎖,那么訪問A的時候就不能訪問B,因此鎖選擇很重要。

4、Monitor

其實lock關(guān)鍵字就是對Monitor的簡化調(diào)用,lock最終會被編譯成Monitor,因此一般不直接使用Monitor類,看下面代碼:

usingSystem;

usingSystem.Threading;

namespaceMonitorDemo

classProgram

staticintMoney=100;

privatestaticobjectlocker=newobject();

staticvoidQuQian(stringname)

//等待沒有人鎖定locker對象,就鎖定它,然后繼續(xù)執(zhí)行

Monitor.Enter(locker);

Console.WriteLine(name+"查看一下余額"+Money);

intyue=Money-1;

Console.WriteLine(name+"取錢");

Money=yue;

Console.WriteLine(name+"取完了,剩"+Money);

finally

//釋放locker對象的鎖

Monitor.Exit(locker);

staticvoidMain(string[]args)

Threadt1=newThread(()={

for(inti=0;ii++)

QuQian("t1");

Threadt2=newThread(()={

for(inti=0;ii++)

QuQian("t2");

t1.Start();

t2.Start();

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論