c# winform多線程的小例子

在文本框中輸入一個(gè)數(shù)字,點(diǎn)擊開始累加按鈕,程序計(jì)算從1開始累計(jì)到該數(shù)字的結(jié)果。因?yàn)樵摾奂舆^程比較耗時(shí),如果直接在UI線程中進(jìn)行,那么當(dāng)前窗口將出現(xiàn)假死。為了有更好的用戶體驗(yàn),程序啟動(dòng)一個(gè)新的線程來單獨(dú)執(zhí)行該計(jì)算,然后每隔200毫秒讀取一次累加結(jié)果,并把結(jié)果顯示到文本框下方的label控件中。同時(shí),程序支持取消操作,點(diǎn)擊取消累計(jì)按鈕,程序?qū)⑷∠奂硬僮?,并把?dāng)前累加值顯示到label中。為了方便后面的描述,我把UI線程稱作主線程,把執(zhí)行累加計(jì)算的線程稱作工作者線程。該過程有兩個(gè)關(guān)鍵點(diǎn):
1:如何在工作者線程中訪問主線程創(chuàng)建的控件;
2:如何取消比較耗時(shí)的計(jì)算;
為了便于在工作者線程中調(diào)用累加過程,我把它寫成一個(gè)單獨(dú)方法,如下:
/// <summary>
/// 從1累加到指定的值,為了讓該方法支持取消操作所以需要CancellationToken參數(shù)
/// </summary>
/// <param name="countTo">累加到的指定值</param>
/// <param name="ct">取消憑證</param>
private void CountTo(int countTo, CancellationToken ct) {
int sum = 0;
for (; countTo > 0; countTo--) {
if (ct.IsCancellationRequested) {
break;
}
sum += countTo;
//Invoke方法用于獲得創(chuàng)建lbl_Status的線程所在的上下文
this.Invoke(new Action(()=>lbl_Status.Text = sum.ToString()));
Thread.Sleep(200);
}
}
該方法就是用于累加數(shù)字,它有兩個(gè)需要注意的地方
1:方法需要傳遞一個(gè)CancellationToken參數(shù),用于支持取消操作(《clr via c# 3版》中把這種方式稱作協(xié)作式取消,也就是說某一個(gè)操作必須支持取消,然后才能取消該操作);
2:為了允許工作者線程訪問主線程創(chuàng)建的lbl_Status控件,我在該線程中使用this.Invoke方法。該方法用于獲得主線程所創(chuàng)建控件的訪問權(quán)。它需要一個(gè)委托作為參數(shù),在該委托中我們可以定義對(duì)lbl_Status的操作。例如在上例中我就是把當(dāng)前的累加結(jié)果賦給lbl_Status的Text屬性。
然后我們看一下如何在一個(gè)共走著線程中執(zhí)行計(jì)算耗時(shí)的操作,也就是“開始累加”按鈕的操作:
private void btn_Count_Click(object sender, EventArgs e)
{
_cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(state=>CountTo(int.Parse(txt_CountTo.Text),_cts.Token));
}
我使用線程池線程來執(zhí)行該操作,之所以使用線程池線程而不是自己的Threading對(duì)象,是因?yàn)榫€程池默認(rèn)已經(jīng)為我們創(chuàng)建好了一些線程,從而省去創(chuàng)建新線程造成的一些列資源消耗,同時(shí),完成計(jì)算任務(wù)后該線程池線程自動(dòng)回到池中等待下一個(gè)任務(wù)。我把_cts作為一個(gè)成員變量,聲明如下:
private CancellationTokenSource _cts;
它需要引入using System.Threading;命名空間。
取消操作更加簡(jiǎn)單,代碼如下:
private void btn_Cancel_Click(object sender, EventArgs e)
{
if (_cts != null)
_cts.Cancel();
}
這樣我們就完成了在winform中使用多線程的例子,同時(shí)該例子支持取消操作。完整代碼如下:
using System;
using System.Threading;
using System.Windows.Forms;
namespace WinformApp
{
public partial class Form1 : Form
{
private CancellationTokenSource _cts;
public Form1()
{
InitializeComponent();
}
/// <summary>
/// 從1累加到指定的值,為了讓該方法支持取消操作所以需要CancellationToken參數(shù)
/// </summary>
/// <param name="countTo">累加到的指定值</param>
/// <param name="ct">取消憑證</param>
private void CountTo(int countTo, CancellationToken ct) {
int sum = 0;
for (; countTo > 0; countTo--) {
if (ct.IsCancellationRequested) {
break;
}
sum += countTo;
//Invoke方法用于獲得創(chuàng)建lbl_Status的線程所在的上下文
this.Invoke(new Action(()=>lbl_Status.Text = sum.ToString()));
Thread.Sleep(200);
}
}
private void btn_Count_Click(object sender, EventArgs e)
{
_cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(state=>CountTo(int.Parse(txt_CountTo.Text),_cts.Token));
}
private void btn_Cancel_Click(object sender, EventArgs e)
{
if (_cts != null)
_cts.Cancel();
}
private void btn_Pause_Click(object sender, EventArgs e)
{
}
}
}
相關(guān)文章
解析C# Console 控制臺(tái)為什么也會(huì)卡死(原因分析)
在分析旅程中,總會(huì)有幾例控制臺(tái)的意外卡死導(dǎo)致的生產(chǎn)事故,有經(jīng)驗(yàn)的朋友都知道,控制臺(tái)卡死一般是動(dòng)了快速編輯窗口的緣故,雖然知道緣由,但一直沒有時(shí)間探究底層原理,市面上也沒有對(duì)這塊的底層原理介紹,昨天花了點(diǎn)時(shí)間簡(jiǎn)單探究了下,感興趣的朋友一起看看吧2023-10-10
C#獲取兩個(gè)數(shù)的最大公約數(shù)和最小公倍數(shù)示例
本文介紹了使用C#獲取兩個(gè)數(shù)的最大公約數(shù)和最小公倍數(shù)的示例,大家參考使用吧2014-01-01
C#編程實(shí)現(xiàn)帶有Aero效果的窗體示例
這篇文章主要介紹了C#編程實(shí)現(xiàn)帶有Aero效果的窗體,涉及C#調(diào)用動(dòng)態(tài)鏈接庫(kù)針對(duì)窗體屬性的相關(guān)操作技巧,需要的朋友可以參考下2017-07-07
C# 開發(fā)(創(chuàng)藍(lán)253)手機(jī)短信驗(yàn)證碼接口的實(shí)例
下面小編就為大家分享一篇C# 開發(fā)(創(chuàng)藍(lán)253)手機(jī)短信驗(yàn)證碼接口的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01
C#實(shí)現(xiàn)遠(yuǎn)程關(guān)閉和重啟計(jì)算機(jī)的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用C#實(shí)現(xiàn)遠(yuǎn)程關(guān)閉和重啟計(jì)算機(jī)的功能,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C#有一定的幫助,感興趣的小伙伴可以跟隨小編一起了解一下2022-12-12

