日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

14多线程程序设计

發(fā)布時(shí)間:2025/5/22 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 14多线程程序设计 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

多線程程序設(shè)計(jì)


多線程程序設(shè)計(jì)

如果在一個(gè)程序中,有多個(gè)工作要同時(shí)做,可以采用多線程。在Windows操作系統(tǒng)中可以運(yùn)行多個(gè)程序,把一個(gè)運(yùn)行的程序叫做一個(gè)進(jìn)程。一個(gè)進(jìn)程又可以有多個(gè)線程,每個(gè)線程輪流占用CPU的運(yùn)行時(shí)間,Windows操作系統(tǒng)將時(shí)間分為時(shí)間片,一個(gè)線程使用一個(gè)時(shí)間片后,操作系統(tǒng)將此線程掛起,將另一個(gè)線程喚醒,使其使用下一個(gè)時(shí)間片,操作系統(tǒng)不斷的把線程掛起,喚醒,再掛起,再喚醒,如此反復(fù),由于現(xiàn)在CPU的速度比較快,給人的感覺象是多個(gè)線程同時(shí)執(zhí)行。Windows操作系統(tǒng)中有很多這樣的例子,例如復(fù)制文件時(shí),一方面在進(jìn)行磁盤的讀寫操作,同時(shí)一張紙不停的從一個(gè)文件夾飄到另一個(gè)文件夾,這個(gè)飄的動作實(shí)際上是一段動畫,兩個(gè)動作是在不同線程中完成的,也就是說兩個(gè)動作是同時(shí)完成的。又如Word程序中的拼寫檢查也是在另一個(gè)線程中完成的。每個(gè)進(jìn)程最少有一個(gè)線程,叫主線程,是進(jìn)程自動創(chuàng)建的,每進(jìn)程可以創(chuàng)建多個(gè)線程。

不同語言和操作系統(tǒng)對線程提供了不同支持,編寫多線程應(yīng)用程序的方法也不盡相同。例如,VB6沒有提供對線程的支持,程序員不能處理自己的線程。VC++6.0開發(fā)人員必須充分理解Windows線程和處理模型的復(fù)雜性,同時(shí)擁有這種線程模型的強(qiáng)大功能。

.Net Framework提供了一個(gè)完整而且功能強(qiáng)大的線程模型,該模型允許編程人員精確控制線程中運(yùn)行的內(nèi)容,線程何時(shí)退出,以及它將訪問多少數(shù)據(jù)等,但使用比VC++6.0簡單。

7.1?? 線程類(Thread)的屬性和方法

線程類在命名空間System.Threading中定義的,因此如果要創(chuàng)建多線程,必須引入命名空間System.Threading。Thread類的常用屬性和方法如下:

l? 屬性Priority:設(shè)置線程優(yōu)先級,有5種優(yōu)先級類別:AboveNormal(稍高)、BelowNormal(稍低)、Normal(中等,默認(rèn)值)、Highest(最高)和Lowest(最低)。例如語句myThread.Priority=ThreadPriority.Highest設(shè)置線程myThread的優(yōu)先級為最高。一個(gè)線程的優(yōu)先權(quán)并不是越高越好,應(yīng)考慮到整個(gè)進(jìn)程中所有線程以及其他進(jìn)程的情況做出最優(yōu)選擇。優(yōu)先級相同的線程按照時(shí)間片輪流運(yùn)行。優(yōu)先級高的線程先運(yùn)行,只有優(yōu)先級高的線程停止、休眠或暫停時(shí),低優(yōu)先級的線程才能運(yùn)行。

l? 構(gòu)造函數(shù):Thread(new ThreadStart(線程中要執(zhí)行的方法名)),構(gòu)造函數(shù)參數(shù)中指定的方法需要程序員自己定義,這個(gè)方法完成線程所要完成的任務(wù),退出該方法,線程結(jié)束。該方法必須為公有void類型的方法,不能有參數(shù)。

l? 方法Start():建立線程類對象后,線程處于未啟動狀態(tài),這個(gè)方法使線程改變?yōu)榫途w狀態(tài),如果能獲的CPU的運(yùn)行時(shí)間,線程變?yōu)檫\(yùn)行狀態(tài)。

l? 方法IsAlive():判斷線程對象是否存在,=true,線程存在。

l? 方法Abort():撤銷線程對象。不能撤銷一個(gè)已不存在的線程對象,因此在撤銷一個(gè)線程對象前,必須用方法IsAlive()判斷線程對象是否存在。

l? 靜態(tài)方法Sleep():線程休眠參數(shù)指定的時(shí)間,單位為毫秒,此時(shí)線程處于休眠狀態(tài)。線程休眠后,允許其它就緒線程運(yùn)行。休眠指定時(shí)間后,線程變?yōu)榫途w狀態(tài)。

l? 方法Suspend():該方法使線程變?yōu)閽炱馉顟B(tài)。必須用Resume()方法喚醒掛起線程。

l? 方法Resume():該方法使掛起線程變?yōu)榫途w狀態(tài),如果能獲的CPU的運(yùn)行時(shí)間,線程變?yōu)檫\(yùn)行狀態(tài)。如果線程多次被掛起,僅調(diào)用一次Resume()方法就可以把線程喚醒。

7.2?? 創(chuàng)建線程

例子e7_2:本例使用線程類Thread直接創(chuàng)建一個(gè)新的線程,在標(biāo)簽控件中顯示該線程運(yùn)行的時(shí)間。在窗體放置4個(gè)按鈕,單擊按鈕完成新建、掛起、恢復(fù)和停止線程的功能。

(1)??? 新建項(xiàng)目。在窗體中放置4個(gè)按鈕和一個(gè)標(biāo)簽控件,屬性Name分別為:button1、button2、button3、button4和label1,按鈕屬性Text分別為:新線程、掛起、恢復(fù)、撤銷。button1屬性Enabled=true,其余按鈕的屬性Enabled=false。

(2)??? 在Form1.cs頭部增加語句:using System.Threading。

(3)??? 為Form1類定義一個(gè)線程類變量:private Thread thread;

(4)??? 為標(biāo)題為"新線程"的按鈕(button1)增加單擊事件處理函數(shù)如下:

private void button1_Click(object sender, System.EventArgs e)

{?? thread=new Thread(new ThreadStart(fun));//生成線程類對象,fun為自定義方法

label1.Text="0";//運(yùn)行時(shí)間從0開始

thread.Start();//線程變?yōu)榫途w狀態(tài),如能獲的CPU運(yùn)行時(shí)間,線程變?yōu)檫\(yùn)行狀態(tài)

button1.Enabled=false;//標(biāo)題為"新線程"的按鈕,創(chuàng)建線程后,不允許再創(chuàng)建線程

button2.Enabled=true;//標(biāo)題為"掛起"的按鈕,允許對運(yùn)行狀態(tài)的線程掛起

button3.Enabled=false;//標(biāo)題為"恢復(fù)"的按鈕,線程未掛起,不能恢復(fù)

button4.Enabled=true;// 標(biāo)題為"撤銷"的按鈕,允許對運(yùn)行狀態(tài)的線程撤銷

}

(5)??? 為標(biāo)題為"掛起"的按鈕(button2)增加單擊事件處理函數(shù)如下:

private void button2_Click(object sender, System.EventArgs e)

{?? thread. Suspend();//線程暫停(掛起)

button1.Enabled=false;

button2.Enabled=false;

button3.Enabled=true;

button4.Enabled=false;

}

(6)??? 為標(biāo)題為"恢復(fù)"的按鈕(button3)增加單擊事件處理函數(shù)如下:

private void button3_Click(object sender, System.EventArgs e)

{?? thread. Resume();//暫停(掛起)線程恢復(fù)運(yùn)行

button1.Enabled=false;

button2.Enabled=true;

button3.Enabled=false;

button4.Enabled=true;

}

(7)??? 為標(biāo)題為"撤銷"的按鈕(button4)增加單擊事件處理函數(shù)如下:

private void button4_Click(object sender, System.EventArgs e)

{?? if(thread.IsAlive)

{?? thread.Abort();//撤銷線程對象

button1.Enabled=true;

button2.Enabled=false;

button3.Enabled=false;

button4.Enabled=false;

}

}

(8)??? C#線程模型允許將任何一個(gè)void類型的公有方法(靜態(tài)或非靜態(tài))作為線程方法,因此允許在任何一個(gè)類(不要求這個(gè)類是某個(gè)類的子類)中定義線程方法,而且同一個(gè)類中可以定義多個(gè)線程方法。為Form1類定義一個(gè)線程方法如下:

public void fun()//在線程中執(zhí)行的方法,必須為公有void類型方法,不能有參數(shù)。

{?? while(true)//退出該方法,線程結(jié)束,這里是死循環(huán),線程將一直運(yùn)行

{?? int x=Convert.ToInt32(label1.Text);

x++;

label1.Text=Convert.ToString(x);

Thread.Sleep(1000);//線程休眠1秒鐘,休眠一次,線程運(yùn)行了1秒鐘

}

}

(9)??? 在關(guān)閉程序之前,必須撤銷線程對象。為主窗體的Closing事件增加事件處理函數(shù)如下:

private void Form1_Closing(object sender,System.ComponentModel.CancelEventArgs e)

{?? if(thread.IsAlive)

? ? ? ? ? ? ? ? ? ? ? ? thread.Abort();

}

(10) 編譯,運(yùn)行,單擊標(biāo)題為"新線程"的按鈕,新線程開始,計(jì)數(shù)器從0開始計(jì)數(shù)。單擊標(biāo)題為"掛起"的按鈕,線程暫停,計(jì)數(shù)器也暫停。單擊標(biāo)題為"恢復(fù)"的按鈕,線程重新啟動,計(jì)數(shù)器也繼續(xù)計(jì)數(shù)。單擊標(biāo)題為"撤銷"的按鈕,線程對象被撤銷,線程對象不存在,計(jì)數(shù)器停止計(jì)數(shù)。運(yùn)行效果如右圖。

7.3?? 建立線程類

有時(shí)需要建立多個(gè)線程,每個(gè)線程要實(shí)現(xiàn)的功能基本相同,但有個(gè)別參數(shù)不同,例如,每個(gè)線程完成同樣的任務(wù),但控制的對象不同。使用線程類Thread直接創(chuàng)建新線程,線程類構(gòu)造函數(shù)參數(shù)為一個(gè)方法,在這個(gè)方法中實(shí)現(xiàn)線程所要求的任務(wù),但該方法不能有參數(shù),因此無法通過方法的參數(shù)傳遞不同設(shè)置。為解決這個(gè)問題,可以定義一個(gè)自己的線程類。具體實(shí)現(xiàn)方法見下例。下邊的例子用到了進(jìn)度條(ProgressBar)控件,首先介紹進(jìn)度條控件。

7.3.1? 進(jìn)度條(ProgressBar)控件

進(jìn)度條(ProgressBar)控件經(jīng)常用來顯示一個(gè)任務(wù)的進(jìn)度。有時(shí),要完成一個(gè)長時(shí)間的任務(wù),例如一個(gè)軟件的安裝,如果沒有任何提示,使用者可能分不清任務(wù)是在進(jìn)行中,還是死機(jī)了,可以使用進(jìn)度條顯示安裝進(jìn)度,表示安裝正在進(jìn)行。進(jìn)度條常用屬性如下:

l? 屬性Maximum:進(jìn)度條所代表的最大值(整數(shù)),默認(rèn)值100。

l? 屬性Minimum:進(jìn)度條所代表的最小值(整數(shù)),默認(rèn)值0。

l? 屬性Step:變化的步長,默認(rèn)值為10。

l? 屬性Value:進(jìn)度條當(dāng)前位置代表的值。修改該值,達(dá)到一個(gè)Step,進(jìn)度增加一格。

7.3.2? 用線程控制進(jìn)度條

例子e7_3_2:建立兩個(gè)線程,分別控制兩個(gè)進(jìn)度條(ProgressBar)控件,每個(gè)進(jìn)度條的屬性Value變化的速率不一樣。具體實(shí)現(xiàn)步驟如下,運(yùn)行效果如下圖。

(1)?? 新建項(xiàng)目。在Form1.cs頭部增加語句:using System.Threading。

(2)?? 在窗體中放置2個(gè)進(jìn)度條(ProgressBar)控件。屬性Name分別為progressBar1、progressBar2。

(3)?? 在Form1.cs文件e7_3_2命名空間中,Form1類定義的后邊,建立線程類如下:

public class myThread

{???? private int SleepTime;//線程的休眠時(shí)間,從構(gòu)造函數(shù)賦值

private ProgressBar progressBar;//本線程控制哪個(gè)進(jìn)度條,從構(gòu)造函數(shù)賦值

private Thread thread1;

public myThread(int Time,ProgressBar p1)//構(gòu)造函數(shù),

{???? SleepTime=Time;

progressBar=p1;

thread1=new Thread(new ThreadStart(fun));

thread1.Start();

}

public void fun()//在線程中執(zhí)行的方法,必須為公有void類型方法,不能有參數(shù)。

{?? while(progressBar.Value!=100)

{?? progressBar.Value+=1;

Thread.Sleep(SleepTime);

}

}//退出該方法,線程結(jié)束

}

(4)?? 為Form1類增加變量:myThread? myThread1,myThread2。

(5)?? 為Form1類構(gòu)造函數(shù)增加語句如下:

myThread1=new myThread(100,progressBar1);

myThread2=new myThread(200,progressBar2);

(6)?? 編譯,運(yùn)行,可以看到兩個(gè)進(jìn)度條以不同的速度前進(jìn),當(dāng)進(jìn)度條被添滿,線程停止。

7.4?? 多個(gè)線程互斥

多個(gè)線程同時(shí)修改共享數(shù)據(jù)可能發(fā)生錯(cuò)誤。假設(shè)2個(gè)線程分別監(jiān)視2個(gè)入口進(jìn)入的人數(shù),每當(dāng)有人通過入口,線程用C#語句對總?cè)藬?shù)變量執(zhí)行加1操作。一條C#語句可能包含若干機(jī)器語言語句,假設(shè)C#語句加1操作包含的機(jī)器語言語句是:先取總?cè)藬?shù),加1,再存回總?cè)藬?shù)。操作系統(tǒng)可以在一條機(jī)器語言語句結(jié)束后,掛起運(yùn)行的線程。如當(dāng)前總?cè)藬?shù)為5,線程1運(yùn)行,監(jiān)視到有人通過入口,取出總?cè)藬?shù)(此時(shí)為5)后,線程1時(shí)間用完掛起。線程2喚醒,也監(jiān)視到有人通過入口,并完成了總?cè)藬?shù)加1并送回的操作,總?cè)藬?shù)為6,線程2掛起。線程1喚醒,對已取出的總?cè)藬?shù)(此時(shí)為5)加1,存回總?cè)藬?shù),總?cè)藬?shù)應(yīng)為7,實(shí)際為6,少算一個(gè)。為了防止此類錯(cuò)誤,在一個(gè)線程修改共享資源(例如上例的總?cè)藬?shù)變量)時(shí),不允許其它線程對同一共享資源進(jìn)行修改,這叫線程的互斥。這樣的實(shí)例很多,例如計(jì)算機(jī)中的許多外設(shè),網(wǎng)絡(luò)中的打印機(jī)等都是共享資源,只允許一個(gè)進(jìn)程或線程使用。

7.4.1? 多個(gè)線程同時(shí)修改共享數(shù)據(jù)可能發(fā)生錯(cuò)誤

例子e7_4_1:下邊的例子模擬2個(gè)線程同時(shí)修改同一個(gè)共享數(shù)據(jù)時(shí)可能發(fā)生的錯(cuò)誤。

(1)?? 新建項(xiàng)目。在Form1.cs頭部增加語句:using System.Threading。

(2)?? 在窗體中放置一個(gè)標(biāo)簽控件,屬性Name=label1。

(3)?? 為Form1類定義2個(gè)線程類變量:Thread thread1,thread2。定義整形變量:int num=0。

(4)?? 為Form1類構(gòu)造函數(shù)增加語句如下:

thread1= new Thread(new ThreadStart(Fun1));

thread2= new Thread(new ThreadStart(Fun2));

thread1.Start();

thread2.Start();

(5)?? 為Form1類中定義Fun1()和Fun2()方法如下:

public void Fun1()//在線程中執(zhí)行的方法,必須為公有void類型方法,不能有參數(shù)。

{?? int k,n;

for(k=0;k<4;k++)

{?? n=num;//取出num,可以把把num想象為總?cè)藬?shù)

n++;//加1

Thread.Sleep(10);//模擬復(fù)雜的費(fèi)時(shí)運(yùn)算,在此期間,有可能時(shí)間片用完

num=n;//存回num

Thread.Sleep(50);

}

label1.Text=Convert.ToString(num);

}//退出該方法,線程結(jié)束

public void Fun2()

{?? int k,n;

for(k=0;k<4;k++)

{?? n=num;

n++;

Thread.Sleep(10);

num=n;

Thread.Sleep(100);

}

label1.Text=Convert.ToString(num);

}

(6)?? 編譯,運(yùn)行,標(biāo)簽控件應(yīng)顯示8,實(shí)際運(yùn)行多次,顯示的數(shù)要小于8。

7.4.2? 用Lock語句實(shí)現(xiàn)互斥

Lock語句的形式如下:lock(e){訪問共享資源的代碼}。其中e指定要鎖定的對象,必須是引用類型,一般為this,即Lock語句所在類的對象。Lock語句將訪問共享資源的代碼標(biāo)記為臨界區(qū)。臨界區(qū)的意義是:假設(shè)線程1正在執(zhí)行e對象的臨界區(qū)中的代碼時(shí),如其它線程也要求執(zhí)行這個(gè)e對象的任何臨界區(qū)中代碼,將被阻塞,一直到線程1退出臨界區(qū)。

例子e7_4_2:用C#語句Lock實(shí)現(xiàn)互斥。修改例子e7_4_1中的Fun1()和Fun2()方法如下:

public void Fun1()//在線程中執(zhí)行的方法,必須為公有void類型方法,不能有參數(shù)。

{?? int k,n;

for(k=0;k<4;k++)

{?? lock(this)//這里的this是Form1類的對象

{?? n=num;//這對大括號中代碼為this的臨界區(qū)

n++;//this的臨界區(qū)包含兩部分,函數(shù)Fun1和Fun2中的臨界區(qū)

Thread.Sleep(10);

num=n;

}

Thread.Sleep(50);

}

label1.Text=Convert.ToString(num);

}//退出該方法,線程結(jié)束

public void Fun2()

{?? int k,n;

for(k=0;k<4;k++)

{?? lock(this)//如有線程進(jìn)入此臨界區(qū),其它線程就不能進(jìn)入這個(gè)臨界區(qū)

{?? n=num;//也不能進(jìn)入前邊的臨界區(qū)

n++;

Thread.Sleep(10);

num=n;

}

Thread.Sleep(100);

}

label1.Text=Convert.ToString(num);

}

編譯,運(yùn)行,標(biāo)簽控件顯示8。如果有多個(gè)共享數(shù)據(jù)區(qū),使用此方法不太方便。

7.4.3? 用Mutex類實(shí)現(xiàn)互斥

可以使用Mutex類對象保護(hù)共享資源(如上例中的總?cè)藬?shù)變量)不被多個(gè)線程同時(shí)訪問。Mutex類WaitOne方法和ReleaseMutex方法之間代碼是互斥體,這些代碼要訪問共享資源。Mutex類的WaitOne方法分配互斥體訪問權(quán),該方法只向一個(gè)線程授予對互斥體的獨(dú)占訪問權(quán)。如果一個(gè)線程獲取了互斥體,則要獲取該互斥體的第二個(gè)線程將被掛起,直到第一個(gè)線程用ReleaseMutex方法釋放該互斥體。

例子e7_4_3:使用Mutex類對象實(shí)現(xiàn)互斥。修改例子e7_4_1,為Form1類增加私有Mutex類變量:private Mutex mut。在Form1類構(gòu)造函數(shù)中建立Mutex類對象,在建立線程語句之前增加語句mut=new Mutex();修改例子e7_4_1中的兩個(gè)Fun1()和Fun2()方法如下:

public void Fun1()//在線程中執(zhí)行的方法,必須為公有void類型方法,不能有參數(shù)。

{?? int k,n;

for(k=0;k<4;k++)

{?? mut.WaitOne();//等待互斥體訪問權(quán)

n=num;// mut.WaitOne()和mut.ReleaseMutex()之間是互斥體

n++;//Mutex類對象mut的互斥體包含兩部分,函數(shù)Fun1和Fun2中的互斥體

Thread.Sleep(10);//有線程進(jìn)入一個(gè)互斥體,其它線程不能進(jìn)入任何一個(gè)互斥體

num=n;

mut.ReleaseMutex();//釋放互斥體訪問權(quán)

Thread.Sleep(50);

}

label1.Text=Convert.ToString(num);

}//退出該方法,線程結(jié)束

public void Fun2()

{?? int k,n;

for(k=0;k<4;k++)

{?? mut.WaitOne();

n=num;

n++;

Thread.Sleep(10);

num=n;

mut.ReleaseMutex();

Thread.Sleep(100);

}

label1.Text=Convert.ToString(num);

}

編譯,運(yùn)行,標(biāo)簽控件顯示8。如果有多個(gè)共享數(shù)據(jù)區(qū),可以定義多個(gè)Mutex類對象。

7.4.4? 用Monitor類實(shí)現(xiàn)互斥

也可以使用Monitor類保護(hù)共享資源不被多個(gè)線程或進(jìn)程同時(shí)訪問。Monitor類通過向單個(gè)線程授予對象鎖來控制對對象的訪問。只有擁有對象鎖的線程才能執(zhí)行臨界區(qū)的代碼,此時(shí)其它任何線程都不能獲取該對象鎖。只能使用Monitor類中的靜態(tài)方法,不能創(chuàng)建Monitor類的實(shí)例。Monitor類中的靜態(tài)方法主要有:

l 方法Enter:獲取參數(shù)指定對象的對象鎖。此方法放在臨界區(qū)的開頭。如其它線程已獲取對象鎖,則該線程將被阻塞,直到其它線程釋放對象鎖,才能獲取對象鎖。

l 方法Wait:釋放參數(shù)指定對象的對象鎖,以便允許其它被阻塞的線程獲取對象鎖。該線程進(jìn)入等待狀態(tài),等待狀態(tài)必須由其它線程用方法Pulse或PulseAll喚醒,使等待狀態(tài)線程變?yōu)榫途w狀態(tài)。

l 方法Pulse和PulseAll:向等待線程隊(duì)列中第一個(gè)或所有等待參數(shù)指定對象的對象鎖的線程發(fā)送信息,占用對象鎖的線程準(zhǔn)備釋放對象鎖。執(zhí)行方法Exit后將釋放對象鎖。

l 方法Exit:釋放參數(shù)指定對象的對象鎖。此操作還標(biāo)記受對象鎖保護(hù)的臨界區(qū)的結(jié)尾。

使用Monitor類實(shí)現(xiàn)互斥也很簡單,請讀者修改例子7_4_1,使用Monitor類實(shí)現(xiàn)互斥。Monitor類主要用來實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者關(guān)系中的線程的同步,具體例子見下一節(jié)。

7.5?? 生產(chǎn)者線程和消費(fèi)者線程的同步

在生產(chǎn)者和消費(fèi)者關(guān)系中,生產(chǎn)者線程產(chǎn)生數(shù)據(jù),并把數(shù)據(jù)存到公共數(shù)據(jù)區(qū),消費(fèi)者線程使用數(shù)據(jù),從公共數(shù)據(jù)區(qū)取出數(shù)據(jù),并進(jìn)行分析。很顯然,如果公共數(shù)據(jù)區(qū)只能存一個(gè)數(shù)據(jù),那么在消費(fèi)者線程取出數(shù)據(jù)前,生產(chǎn)者線程不能放新數(shù)據(jù)到公共數(shù)據(jù)區(qū),否則消費(fèi)者線程將丟失數(shù)據(jù)。同樣,只有在生產(chǎn)者線程把數(shù)據(jù)已經(jīng)放到公共數(shù)據(jù)區(qū),消費(fèi)者線程才能取出數(shù)據(jù),如果新數(shù)據(jù)未放到公共數(shù)據(jù)區(qū),消費(fèi)者線程不能取數(shù)據(jù)。這些就是所謂的生產(chǎn)者和消費(fèi)者關(guān)系,必須要求生產(chǎn)者線程和消費(fèi)者線程同步。

7.5.1? 生產(chǎn)者線程和消費(fèi)者線程不同步可能發(fā)生錯(cuò)誤

例子e7_5_1:下邊的例子模擬生產(chǎn)者線程和消費(fèi)者線程不同步可能發(fā)生錯(cuò)誤。有一個(gè)公共變量,要求生產(chǎn)者線程順序放1到4到這個(gè)公共變量中,每放一個(gè)變量,消費(fèi)者線程取出這個(gè)數(shù)求和,最后把和顯示出來,顯然和應(yīng)為10。如不采取同步措施,和的結(jié)果不正確。

(1)?? 新建項(xiàng)目。在Form1.cs頭部增加語句:using System.Threading。

(2)?? 在窗體中放置一個(gè)標(biāo)簽控件,屬性Name=label1。

(3)?? 為Form1類定義2個(gè)線程類變量:Thread thread1,thread2。

(4)?? 為Form1類定義2個(gè)整形變量:int sum=0,x=-1。

(5)?? 為Form1類構(gòu)造函數(shù)增加語句如下:

thread1= new Thread(new ThreadStart(Fun1));

thread2= new Thread(new ThreadStart(Fun2));

thread1.Start();

thread2.Start();

(6)?? 為Form1類定義Fun1()和Fun2()方法如下:

public void Fun1()//生產(chǎn)數(shù)據(jù)

{?? int k,n;

for(k=1;k<5;k++)

{?? x=k;

Thread.Sleep(200);

}

}

public void Fun2()//消費(fèi)數(shù)據(jù)

{?? int k,n;

for(k=0;k<4;k++)

{?? sum+=x;

Thread.Sleep(100);

}

label1.Text=Convert.ToString(sum);

}

(7)?? 編譯,運(yùn)行,標(biāo)簽控件應(yīng)顯示10,實(shí)際運(yùn)行多次,顯示的數(shù)不為10。

7.5.2? 生產(chǎn)者線程和消費(fèi)者線程同步的實(shí)現(xiàn)

修改上例,為Form1類定義1個(gè)布爾變量:bool mark=false。其值為false,表示數(shù)據(jù)還未放到公共數(shù)據(jù)區(qū)(即x)中,生產(chǎn)者線程可以放數(shù)據(jù)到公共數(shù)據(jù)區(qū)中,由于沒有數(shù)據(jù),消費(fèi)線程不能取數(shù)據(jù),必須等待。mark=true,表示數(shù)據(jù)已放到公共數(shù)據(jù)區(qū)(即x)中,消費(fèi)線程還未取數(shù)據(jù),生產(chǎn)者線程不能再放數(shù)據(jù)到公共數(shù)據(jù)區(qū)中,必須等待。由于有了數(shù)據(jù),消費(fèi)線程可以取數(shù)據(jù)。修改Fun1()如下:

public void Fun1()//生產(chǎn)數(shù)據(jù)

{?? int k,n;

for(k=1;k<5;k++)

{?? Monitor.Enter(this);//這里this是Form1類對象,得到this的對象鎖

if(mark)//Monitor.Enter(this)和Monitor.Exit(this)是臨界區(qū)

Monitor.Wait(this);//如消費(fèi)者數(shù)據(jù)未取走,釋放對象鎖,生產(chǎn)者等待

Mark=!mark;

x=k;

Monitor.Pulse(this);//激活消費(fèi)者線程

Monitor.Exit(this);//釋放this的對象鎖

}

}

修改Fun2()如下:

public void Fun2()//消費(fèi)數(shù)據(jù)

{?? int k,n;

for(k=0;k<4;k++)

{?? Monitor.Enter(this);

if(!mark)

Monitor.Wait(this);//如果生產(chǎn)者未放數(shù)據(jù),消費(fèi)者等待

Mark=!mark;

sum+=x;

Monitor.Pulse(this);

Monitor.Exit(this);

}

label1.Text=Convert.ToString(sum);

}

編譯,運(yùn)行,標(biāo)簽控件應(yīng)顯示10。

轉(zhuǎn)載于:https://www.cnblogs.com/Aha-Best/p/10931701.html

總結(jié)

以上是生活随笔為你收集整理的14多线程程序设计的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。