国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > 綜合 > 正文

利用 Visual C# 創(chuàng)作簡單的多線程組件(轉(zhuǎn)載于MSDN)

2024-07-21 02:19:08
字體:
供稿:網(wǎng)友
可以編寫能同時執(zhí)行多個任務(wù)的應(yīng)用程序。此能力(稱為“多線程處理”或“自由線程處理”)是設(shè)計處理器密集型且要求用戶輸入的組件的強(qiáng)大方法。計算工資表信息的組件就是一個可能利用多線程處理的組件示例。該組件可以在一個線程上處理用戶輸入到數(shù)據(jù)庫的數(shù)據(jù),而在另一個線程上執(zhí)行頻繁使用處理器的工資表計算。通過在不同的線程上運(yùn)行這些進(jìn)程,用戶不必等到計算機(jī)完成計算,就可以輸入其他數(shù)據(jù)。在本演練中,將創(chuàng)建一個簡單的多線程組件,該組件可以同時執(zhí)行若干個復(fù)雜計算。

創(chuàng)建項目
應(yīng)用程序?qū)▎蝹€窗體和一個組件。用戶將輸入值并指示該組件開始計算。然后,窗體將接收來自該組件的值,將其顯示在標(biāo)簽控件中。該組件將執(zhí)行頻繁使用處理器的計算,并在完成后通知窗體。您將在組件中創(chuàng)建公共變量,用以保存從用戶界面收到的值。同時,您還將在組件中實現(xiàn)一些方法,根據(jù)這些變量的值執(zhí)行計算。

注意 盡管對于計算值的方法來說,函數(shù)通常更為可取,但不能在線程之間傳遞參數(shù),也不能返回值。有很多向線程提供值和從線程接收值的簡單方法。在本演示中,將通過更新公共變量將值返回到用戶界面,當(dāng)線程執(zhí)行完畢后,使用事件來通知主程序。
創(chuàng)建窗體

創(chuàng)建新的“windows 應(yīng)用程序”項目。
將應(yīng)用程序命名為 calculations,并將 form1.cs 重命名為 frmcalculations.cs。
該窗體將用作應(yīng)用程序的主用戶界面。

雙擊設(shè)計器上的窗體以打開代碼編輯器。在“編輯”菜單中,選擇“查找和替換”,然后選擇“替換”。使用“全部替換”將 form1 替換為 frmcalculations。
在“解決方案資源管理器”中,右擊“frmcalculations.cs”并選擇“視圖設(shè)計器”。設(shè)計器打開。
向窗體中添加 5 個 label 控件、4 個 button 控件和 1 個 textbox 控件。
為這些控件設(shè)置屬性,如下所示:
控件 名稱 文本
label1 lblfactorial1 (空白)
label2 lblfactorial2 (空白)
label3 lbladdtwo (空白)
label4 lblrunloops (空白)
label5 lbltotalcalculations (空白)
button1 btnfactorial1 factorial
button2 btnfactorial2 factorial - 1
button3 btnaddtwo add two
button4 btnrunloops run a loop
textbox1 txtvalue (空白)

創(chuàng)建 calculator 組件

從“項目”菜單中選擇“添加組件”。
將組件命名為 calculator。
向 calculator 組件添加公共變量

為 calculator 打開代碼編輯器。
添加創(chuàng)建公共變量的語句,這些變量用于將值從 frmcalculations 傳遞給每個線程。
變量 vartotalcalculations 將保留該組件執(zhí)行的計算總數(shù)的累計值,而其他變量將接收來自窗體的值。

public int varaddtwo;
public int varfact1;
public int varfact2;
public int varloopvalue;
public double vartotalcalculations = 0;
向 calculator 組件添加方法和事件

為事件聲明委托,組件將使用這些事件向窗體傳遞值。
注意 盡管您將聲明 4 個事件,但由于其中的兩個事件將具有相同的簽名,因此只需要創(chuàng)建 3 個委托。
緊接著上一步輸入的變量聲明的下方,鍵入下列代碼:

// this delegate will be invoked with two of your events.
public delegate void factorialcompletehandler(double factorial, double totalcalculations);
public delegate void addtwocompletehandler(int result, double totalcalculations);
public delegate void loopcompletehandler(double totalcalculations, int counter);
聲明組件將用來與應(yīng)用程序進(jìn)行通信的事件。為實現(xiàn)此目的,緊接著上一步輸入的代碼的下方,添加下列代碼。
public event factorialcompletehandler factorialcomplete;
public event factorialcompletehandler factorialminusonecomplete;
public event addtwocompletehandler addtwocomplete;
public event loopcompletehandler loopcomplete;
緊接著上一步鍵入的代碼的下方,鍵入下列代碼:
// this method will calculate the value of a number minus 1 factorial
// (varfact2-1!).
public void factorialminusone()
{
double vartotalasofnow = 0;
double varresult = 1;
// performs a factorial calculation on varfact2 - 1.
for (int varx = 1; varx <= varfact2 - 1; varx++)
{
varresult *= varx;
// increments vartotalcalculations and keeps track of the current
// total as of this instant.
vartotalcalculations += 1;
vartotalasofnow = vartotalcalculations;
}
// signals that the method has completed, and communicates the
// result and a value of total calculations performed up to this
// point.
factorialminusonecomplete(varresult, vartotalasofnow);
}

// this method will calculate the value of a number factorial.
// (varfact1!)
public void factorial()
{
double varresult = 1;
double vartotalasofnow = 0;
for (int varx = 1; varx <= varfact1; varx++)
{
varresult *= varx;
vartotalcalculations += 1;
vartotalasofnow = vartotalcalculations;
}
factorialcomplete(varresult, vartotalasofnow);
}

// this method will add two to a number (varaddtwo+2).
public void addtwo()
{
double vartotalasofnow = 0;
int varresult = varaddtwo + 2;
vartotalcalculations += 1;
vartotalasofnow = vartotalcalculations;
addtwocomplete(varresult, vartotalasofnow);
}

// this method will run a loop with a nested loop varloopvalue times.
public void runaloop()
{
int varx;
double vartotalasofnow = 0;
for (varx = 1; varx <= varloopvalue; varx++)
{
// this nested loop is added solely for the purpose of slowing down
// the program and creating a processor-intensive application.
for (int vary = 1; vary <= 500; vary++)
{
vartotalcalculations += 1;
vartotalasofnow = vartotalcalculations;
}
}
loopcomplete(vartotalasofnow, varloopvalue);
}
將用戶輸入傳輸?shù)浇M件
下一步是向 frmcalculations 添加代碼,以接收用戶輸入,以及從 calculator 組件接收值和向它傳輸值。

實現(xiàn) frmcalculations 的前端功能

在代碼編輯器中打開 frmcalculations。
找到 public class frmcalculations 語句。緊接著 { 的下方鍵入:
calculator calculator1;
找到構(gòu)造函數(shù)。緊接著 } 之前,添加以下行:
// creates a new instance of calculator.
calculator1 = new calculator();
在設(shè)計器中單擊每個按鈕,為每個控件的單擊事件處理程序生成代碼大綱,并添加代碼以創(chuàng)建這些處理程序。
完成后,單擊事件處理程序應(yīng)該類似于以下形式:

private void btnfactorial1_click(object sender, system.eventargs e)
// passes the value typed in the txtvalue to calculator.varfact1.
{
calculator1.varfact1 = int.parse(txtvalue.text);
// disables the btnfactorial1 until this calculation is complete.
btnfactorial1.enabled = false;
calculator1.factorial();
}

private void btnfactorial2_click(object sender, system.eventargs e)
{
calculator1.varfact2 = int.parse(txtvalue.text);
btnfactorial2.enabled = false;
calculator1.factorialminusone();
}
private void btnaddtwo_click(object sender, system.eventargs e)
{
calculator1.varaddtwo = int.parse(txtvalue.text);
btnaddtwo.enabled = false;
calculator1.addtwo();
}
private void btnrunloops_click(object sender, system.eventargs e)
{
calculator1.varloopvalue = int.parse(txtvalue.text);
btnrunloops.enabled = false;
// lets the user know that a loop is running
lblrunloops.text = "looping";
calculator1.runaloop();
}
在上一步添加的代碼的下方,鍵入以下代碼以處理窗體將從 calculator1 接收的事件:
protected void factorialhandler(double value, double calculations)
// displays the returned value in the appropriate label.
{
lblfactorial1.text = value.tostring();
// re-enables the button so it can be used again.
btnfactorial1.enabled = true;
// updates the label that displays the total calculations performed
lbltotalcalculations.text = "totalcalculations are " +
calculations.tostring();
}

protected void factorialminushandler(double value, double calculations)
{
lblfactorial2.text = value.tostring();
btnfactorial2.enabled = true;
lbltotalcalculations.text = "totalcalculations are " +
calculations.tostring();
}

protected void addtwohandler(int value, double calculations)
{
lbladdtwo.text = value.tostring();
btnaddtwo.enabled = true;
lbltotalcalculations.text = "totalcalculations are " +
calculations.tostring();
}

protected void loopdonehandler(double calculations, int count)
{
btnrunloops.enabled = true;
lblrunloops.text = count.tostring();
lbltotalcalculations.text = "totalcalculations are " +
calculations.tostring();
}
在 frmcalculations 的構(gòu)造函數(shù)中,緊挨在 } 之前添加下列代碼,以處理窗體將從 calculator1 接收的自定義事件:
calculator1.factorialcomplete += new
calculator.factorialcompletehandler(this.factorialhandler);
calculator1.factorialminusonecomplete += new
calculator.factorialcompletehandler(this.factorialminushandler);
calculator1.addtwocomplete += new
calculator.addtwocompletehandler(this.addtwohandler);
calculator1.loopcomplete += new
calculator.loopcompletehandler(this.loopdonehandler);
測試應(yīng)用程序
現(xiàn)在項目已經(jīng)創(chuàng)建完畢,該項目將能夠執(zhí)行若干個復(fù)雜計算的組件與窗體結(jié)合在一起。盡管尚未實現(xiàn)多線程處理功能,但在繼續(xù)之前應(yīng)該對項目進(jìn)行測試,以驗證它的功能。

測試項目

從“調(diào)試”菜單中選擇“啟動”。
應(yīng)用程序啟動并顯示 frmcalculations。

在文本框中鍵入 4,然后單擊標(biāo)記為“添加兩個”的按鈕。
在按鈕下方的標(biāo)簽中應(yīng)該顯示數(shù)字“6”,在 lbltotalcalculations 中應(yīng)該顯示“total calculations are 1”。

現(xiàn)在單擊標(biāo)記為“階乘 - 1”的按鈕。
該按鈕的下方應(yīng)顯示數(shù)字“6”,而 lbltotalcalculations 中現(xiàn)在應(yīng)顯示“total calculations are 4”。

將文本框中的值更改為 20,然后單擊標(biāo)記為“階乘”的按鈕。
該按鈕的下方顯示數(shù)字“2.43290200817664e+18”,而 lbltotalcalculations 中現(xiàn)在顯示為“total calculations are 24”。

將文本框中的值更改為 50000,然后單擊標(biāo)記為“運(yùn)行循環(huán)”的按鈕。
注意,在此按鈕重新啟用前有一個短暫然而明顯的間隔。此按鈕下的標(biāo)簽應(yīng)顯示“50000”,而總的計算次數(shù)顯示為“25000024”。

將文本框中的值更改為 5000000 并單擊標(biāo)記為“運(yùn)行循環(huán)”的按鈕,緊接著單擊標(biāo)記為“添加兩個”的按鈕。再次單擊它。
直到循環(huán)已經(jīng)完成,該按鈕以及窗體上的任何控件才有響應(yīng)。

如果程序只運(yùn)行單個執(zhí)行線程,則類似上述示例的頻繁使用處理器的計算傾向于占用該程序,直到計算已經(jīng)完成。在下一節(jié)中,您將向應(yīng)用程序添加多線程處理功能,以便一次可以運(yùn)行多個線程。

添加多線程處理功能
上面的示例演示了只運(yùn)行單個執(zhí)行線程的應(yīng)用程序的限制。在下一節(jié),您將使用 thread 類對象向組件添加多個執(zhí)行線程。

添加 threads 子例程

在代碼編輯器中打開 calculator.cs。
在代碼頂部附近,找到類聲明,緊接著 { 的下方,鍵入下列代碼:
// declares the variables you will use to hold your thread objects.
public system.threading.thread factorialthread;
public system.threading.thread factorialminusonethread;
public system.threading.thread addtwothread;
public system.threading.thread loopthread;
在代碼底部緊接著類聲明結(jié)尾之前,添加以下方法:
public void choosethreads(int threadnumber)
{
// determines which thread to start based on the value it receives.
switch(threadnumber)
{
case 1:
// sets the thread using the addressof the subroutine where
// the thread will start.
factorialthread = new system.threading.thread(new
system.threading.threadstart(this.factorial));
// starts the thread.
factorialthread.start();
break;
case 2:
factorialminusonethread = new
system.threading.thread(new
system.threading.threadstart(this.factorialminusone));
factorialminusonethread.start();
break;
case 3:
addtwothread = new system.threading.thread(new
system.threading.threadstart(this.addtwo));
addtwothread.start();
break;
case 4:
loopthread = new system.threading.thread(new
system.threading.threadstart(this.runaloop));
loopthread.start();
break;
}
}
當(dāng)實例化 thread 對象時,它要求一個 threadstart 對象形式的參數(shù)。threadstart 對象是一個指向開始線程的方法的地址的委托。threadstart 對象不能接受參數(shù)或者傳遞值,因此只能表示 void 方法。剛才實現(xiàn)的 choosethreads 方法將從調(diào)用它的程序接收一個值,并使用該值來確定要啟動的適當(dāng)線程。

向 frmcalculations 添加適當(dāng)?shù)拇a

在代碼編輯器中打開 frmcalculations.cs 文件,然后找到 protected void btnfactorial1_click。
注釋掉直接調(diào)用 calculator1.factorial1 方法的行,如下所示:
// calculator1.factorial()
添加下列行,以調(diào)用 calculator1.choosethreads 方法:
// passes the value 1 to calculator1, thus directing it to start the
// correct thread.
calculator1.choosethreads(1);
對其他 button_click 子例程作類似的修改。
注意 一定要為 threads 參數(shù)包含適當(dāng)?shù)闹怠?br>完成后,代碼看起來應(yīng)該類似以下形式:

protected void btnfactorial1_click(object sender, system.eventargs e)
// passes the value typed in the txtvalue to calculator.varfact1
{
calculator1.varfact1 = int.parse(txtvalue.text);
// disables the btnfactorial1 until this calculation is complete
btnfactorial1.enabled = false;
// calculator1.factorial();
calculator1.choosethreads(1);
}

protected void btnfactorial2_click(object sender, system.eventargs e)
{
calculator1.varfact2 = int.parse(txtvalue.text);
btnfactorial2.enabled = false;
// calculator1.factorialminusone();
calculator1.choosethreads(2);
}
protected void btnaddtwo_click(object sender, system.eventargs e)
{
calculator1.varaddtwo = int.parse(txtvalue.text);
btnaddtwo.enabled = false;
// calculator1.addtwo();
calculator1.choosethreads(3);
}

protected void btnrunloops_click(object sender, system.eventargs e)
{
calculator1.varloopvalue = int.parse(txtvalue.text);
btnrunloops.enabled = false;
// lets the user know that a loop is running
lblrunloops.text = "looping";
// calculator1.runaloop();
calculator1.choosethreads(4);
}
封送處理對控件的調(diào)用
現(xiàn)在將加速窗體上的顯示更新。鑒于控件總是由主執(zhí)行線程所有,從屬線程中對控件的任何調(diào)用都需要“封送處理”調(diào)用。封送處理是跨線程邊界移動調(diào)用的行為,需要耗費大量的資源。為了使需要發(fā)生的封送處理量減到最少,并確保以線程安全方式處理調(diào)用,應(yīng)使用 control.begininvoke 方法來調(diào)用主執(zhí)行線程上的方法,從而使必須發(fā)生的跨線程邊界的封送處理量減到最少。當(dāng)調(diào)用操作控件的方法時,這種調(diào)用非常必要。有關(guān)詳細(xì)信息,請參見從線程操作控件。

創(chuàng)建控件調(diào)用過程

為 frmcalculations 打開代碼編輯器。在聲明部分,添加下列代碼:
public delegate void fhandler(double value, double calculations);
public delegate void a2handler(int value, double calculations);
public delegate void ldhandler(double calculations, int count);
invoke 和 begininvoke 需要將適當(dāng)方法的委托作為參數(shù)。這些代碼行聲明一些委托簽名,這些簽名將被 begininvoke 用于調(diào)用適當(dāng)?shù)姆椒ā?

在代碼中添加下列空方法。
public void facthandler(double value, double calculations)
{
}
public void fact1handler(double value, double calculations)
{
}
public void add2handler(int value, double calculations)
{
}
public void ldonehandler(double calculations, int count)
{
}
在“編輯”菜單中,使用“剪切”和“粘貼”,從 factorialhandler 方法中剪切所有代碼,并將其粘貼到 facthandler 中。
對 factorialminushandler 和 fact1handler、addtwohandler 和 add2handler 以及 loopdonehandler 和 ldonehandler 重復(fù)上面的步驟。
完成后,在 factorialhandler、factorial1handler、addtwohandler 和 loopdonehandler 中應(yīng)該沒有剩余代碼,并且它們曾經(jīng)包含的所有代碼應(yīng)該已經(jīng)移動到適當(dāng)?shù)男路椒ㄖ小?

調(diào)用 begininvoke 方法以異步調(diào)用這些方法。可以從窗體 (this) 或者窗體上的任何控件調(diào)用 begininvoke。
完成后,代碼看起來應(yīng)該類似以下形式:

protected void factorialhandler(double value, double calculations)
{
// begininvoke causes asynchronous execution to begin at the address
// specified by the delegate. simply put, it transfers execution of
// this method back to the main thread. any parameters required by
// the method contained at the delegate are wrapped in an object and
// passed.
this.begininvoke(new fhandler(facthandler), new object[]
{value, calculations});
}
protected void factorialminushandler(double value, double calculations)
{
this.begininvoke(new fhandler(fact1handler), new object []
{value, calculations});
}

protected void addtwohandler(int value, double calculations)
{
this.begininvoke(new a2handler(add2handler), new object[]
{value, calculations});
}

protected void loopdonehandler(double calculations, int count)
{
this.begininvoke(new ldhandler(ldonehandler), new object[]
{calculations, count});
}
看起來似乎事件處理程序僅僅是對下一個方法進(jìn)行調(diào)用。實際上,該事件處理程序?qū)崿F(xiàn)了在主操作線程上調(diào)用方法。這種方法可節(jié)省跨線程邊界的調(diào)用,并使多線程應(yīng)用程序能夠有效運(yùn)行而不必?fù)?dān)心導(dǎo)致死鎖。有關(guān)在多線程環(huán)境下使用控件的詳細(xì)信息,請參見從線程操作控件。

保存您的工作。
從“調(diào)試”菜單中選擇“啟動”,測試該解決方案。
在文本框內(nèi)鍵入 10000000 并單擊“運(yùn)行循環(huán)”。
此按鈕下方的標(biāo)簽中顯示“l(fā)ooping”。運(yùn)行這個循環(huán)應(yīng)該占用很長時間。如果它完成得太快,請相應(yīng)地調(diào)整該數(shù)字的大小。

連續(xù)地快速單擊仍在啟用的三個按鈕。您會發(fā)現(xiàn)所有按鈕都響應(yīng)您的輸入。在“add two”下方的標(biāo)簽應(yīng)該第一個顯示結(jié)果。結(jié)果稍后將顯示在階乘按鈕下方的標(biāo)簽中。估計這些結(jié)果會無限大,因為 10,000,000 的階乘返回的數(shù)字對于雙精度變量而言太大,以至超出了它包含的范圍。最后,再過片刻,結(jié)果將返回到“運(yùn)行循環(huán)”按鈕下方。
正如剛剛觀察到的,在四個單獨的線程上同時執(zhí)行四組獨立的計算。用戶界面保持對輸入的響應(yīng),并在每個線程完成后返回結(jié)果。

協(xié)調(diào)線程
有經(jīng)驗的多線程應(yīng)用程序用戶可能會發(fā)現(xiàn)已鍵入的代碼中存在細(xì)微缺陷。從 calculator.cs 中每個執(zhí)行計算的子例程中撤回以下代碼行:

vartotalcalculations += 1;
vartotalasofnow = vartotalcalculations;
這兩行代碼遞增公共變量 vartotalcalculations 并將局部變量 vartotalasofnow 設(shè)為此值。然后,該值被返回給 frmcalculations,并顯示在標(biāo)簽控件中。但返回的值正確嗎?如果只有單個執(zhí)行線程在運(yùn)行,則答案明顯是正確的。但是如果有多個線程在運(yùn)行,答案則變得不太確定。每個線程都具有遞增變量 vartotalcalculations 的能力。有可能出現(xiàn)這樣的情況:在一個線程遞增該變量之后,但在它將該值復(fù)制到 vartotalasofnow 之前,另一個線程可能通過遞增該變量而更改它的值。這將導(dǎo)致有可能每個線程實際上在報告不正確的結(jié)果。visual c# 提供 lock 語句語句以允許線程的同步,從而確保每個線程始終返回準(zhǔn)確的結(jié)果。lock 的語法如下所示:

lock(anobject)
{
// insert code that affects the object.
// insert more code that affects the object.
// insert more code that affects the object.
// release the lock.
}
輸入 lock 塊后,在指定的線程對所討論的對象擁有專用鎖之前,對指定表達(dá)式的執(zhí)行一直被堵塞。在上面顯示的示例中,對 anobject 的執(zhí)行處于鎖定狀態(tài)。必須對返回引用的對象(而非返回值的對象)使用 lock。然后,執(zhí)行以塊的形式繼續(xù)進(jìn)行,而不會受到其他線程的干擾。作為一個單元執(zhí)行的語句集稱為“原子”。當(dāng)遇到 } 時,表達(dá)式將被釋放,線程可繼續(xù)正常工作。

將 lock 語句添加到應(yīng)用程序

在代碼編輯器中打開 calculator.cs。
找到下列代碼的每個實例:
vartotalcalculations += 1;
vartotalasofnow = vartotalcalculations;
應(yīng)該有此代碼的四個實例,每個計算方法中有一個。

修改此代碼,使其顯示為如下形式:
lock(this)
{
vartotalcalculations += 1;
vartotalasofnow = vartotalcalculations;
}
保存工作,并按上例所示進(jìn)行測試。
您可能注意到對程序性能的細(xì)微影響。這是因為當(dāng)組件獲得排他鎖后,線程的執(zhí)行停止。盡管它保證了正確性,但這種方法抵消了多線程帶來的某些性能優(yōu)點。應(yīng)該認(rèn)真考慮鎖定線程的必要性,并且僅當(dāng)絕對必要時才予以實現(xiàn)。



發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 武威市| 昌吉市| 称多县| 乐昌市| 塔河县| 易门县| 和静县| 林周县| 大城县| 焉耆| 那坡县| 许昌市| 黎平县| 通河县| 汉阴县| 长寿区| 武山县| 宁陕县| 文昌市| 靖江市| 丹江口市| 鹤壁市| 廉江市| 安国市| 旌德县| 滦平县| 磐石市| 清丰县| 民权县| 馆陶县| 白朗县| 东平县| 青海省| 舞阳县| 天台县| 奎屯市| 呼伦贝尔市| 麻城市| 衡阳市| 凉山| 昭苏县|