您好,登錄后才能下訂單哦!
這篇文章主要講解了“C#多線程Task的使用”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“C#多線程Task的使用”吧!
背景
當(dāng)我們程序在執(zhí)行長時間數(shù)據(jù)處理時,如果只用主進程,會出現(xiàn)卡死或是假死等待時間長的情況,如果這個時間我們用到多線程操作,不但可以減少耗時,執(zhí)行過程中可以簡單同步UI界面,讓人看到不會有種假死的狀態(tài).
要求
我們要遍達(dá)30W數(shù)據(jù),分成6個任務(wù),每個任務(wù)處理5W數(shù)據(jù),然后同時并行的任務(wù)是3項.每個任務(wù)遍歷完成后生成一個TXT的文件.
設(shè)計思路
我們通過多個任務(wù)添加多個用戶控件的方法,然后在控件的TAG里設(shè)置為bool類型,用于判斷任務(wù)是否已經(jīng)執(zhí)行了,然后在線程操作的時候首先找到未執(zhí)行的任務(wù),然后更新TAG標(biāo)志并處理任務(wù),處理完當(dāng)前任務(wù)數(shù)據(jù)后再把數(shù)據(jù)寫入到當(dāng)前目錄下的txt文件里面,直到所有的任務(wù)都完成后結(jié)束線程.
知識點
用戶控件的設(shè)計
動態(tài)創(chuàng)建控件
線程Task的操作
編碼
打開VS,我用的是2017,新建一個項目名稱為TaskDemo
在Form里面我們加三個控件,分別是FlowLayoutPanel ,TextBox和一個Button
左邊的FlowLayoutPanel是一會兒我們自己建個用戶控件來顯示任務(wù)列表的,這個可以自適應(yīng)排列.
接下來我們在解決方案里面鼠標(biāo)右鍵選擇添加新建一個用戶控件起名為TaskCtrl
建好后我們在這里面增加幾個Label和一個ProgressBar
在userctrl里定義三個值
//任務(wù)號
public int Taskno;
//開始記錄數(shù)
public int Startrecord;
//結(jié)束記錄數(shù)
public int Endrecord;
然后重新改一下構(gòu)造函數(shù)
public TaskCtrl(int taskno, int startrecord, int endrecord)
{
Taskno = taskno;
Startrecord = startrecord;
Endrecord = endrecord;
InitializeComponent();
lblTaskNo.Text = "任務(wù)號:" + Taskno;
lblRecord.Text = "任務(wù)記錄數(shù):" + (Endrecord - Startrecord);
prgb.Minimum = Startrecord;
prgb.Maximum = Endrecord;
prgb.Value = Startrecord;
}
我們再增加一個方法,用于更新顯示當(dāng)前的任務(wù)
/// <summary>
/// 顯示進度條和信息
/// </summary>
/// <param name="row"></param>
public void ShowProgressbar(int row)
{
prgb.Value = row;
lblStatus.Text = row + "/" + Endrecord;
}
這樣用戶控件我們就完成了.下面開始寫主界面的按鈕事件
首先我們定義幾個值,像總數(shù)據(jù),每項任務(wù)的值,同時并行任務(wù)數(shù)
然后我們再定義一個委托,用于線程執(zhí)行過程中可以同步UI顯示當(dāng)前狀態(tài)的
接下來就是我們開始寫按鈕事件
先寫一個初始化用戶控件的方法
/// <summary>
/// 初始化flowlayoutpanel獲取用戶控件
/// </summary>
private void InitFlowLayoutPanel()
{
flpnl.Controls.Clear();
//計算任務(wù)個數(shù)
//總數(shù)除每個任務(wù)最大數(shù),如果有余數(shù)任務(wù)數(shù)+1,如果不有余數(shù)就是任務(wù)數(shù)
int taskqty = totalrecord % taskrecord > 0 ? totalrecord / taskrecord + 1 : taskrecord / taskrecord;
//循環(huán)自動創(chuàng)建用戶控件
for (int i = 0; i < taskqty; i++)
{
//計算當(dāng)前任務(wù)的開始記錄和結(jié)束記錄
int startrecord = i == 0 ? 1 : i * taskrecord + 1;
int endrecord = i == 0 ? taskrecord : (i + 1) * taskrecord;
TaskCtrl userctrl = new TaskCtrl(i, startrecord, endrecord);
userctrl.Name = "Task" + i;
//Tag標(biāo)志用于記錄當(dāng)前任務(wù)是否已經(jīng)完成
userctrl.Tag = false;
flpnl.Controls.Add(userctrl);
}
}
業(yè)務(wù)處理的核心代碼
/// <summary>
/// 線程操作
/// </summary>
private void TaskModify()
{
Task parent = new Task(() =>
{
var cts = new CancellationTokenSource();
var tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent,
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
//定義數(shù)組執(zhí)行的任務(wù)
Task[] childTasks = new Task[tasknum - 1];
//賦值
for (int task = 0; task < tasknum; task++)
{
childTasks [task] = tf.StartNew(() =>
{
bool res = true;
while (res)
{
Thread.Sleep(2000);
//每次執(zhí)行完后得到返回值用于查詢是否還有未完成任務(wù)
//如果有繼續(xù)執(zhí)行,沒有就退出線程
res = DoTask();
}
}, cts.Token).ContinueWith(t =>
{
if (t.IsCompleted)
{
this.BeginInvoke(TextShow, "當(dāng)前線程任務(wù)都執(zhí)行完畢!!");
}
else if (t.IsCanceled)
{
this.BeginInvoke(TextShow, "當(dāng)前線程取消任務(wù)!!");
}
else if (t.IsFaulted)
{
this.BeginInvoke(TextShow, "當(dāng)前線程異常!!");
}
}, cts.Token);
Thread.Sleep(200);
}
});
parent.Start();
}
數(shù)據(jù)處理的相關(guān)代碼
/// <summary>
/// 獲取用戶控件,判斷是否已經(jīng)執(zhí)行完任務(wù)
/// </summary>
/// <returns></returns>
private TaskCtrl GetUserCtrl()
{
TaskCtrl ctrl = null;
foreach (Control c in flpnl.Controls)
{
ctrl = c as TaskCtrl;
//判斷用戶控件是否已經(jīng)完成任務(wù)
if (ctrl != null && !(bool) ctrl.Tag)
{
return ctrl;
}
}
return null;
}
private bool DoTask()
{
bool res = false;
string str = string.Empty;
//獲取用戶控件,如果不為空即開始任務(wù)
TaskCtrl taskCtrl = GetUserCtrl();
if (taskCtrl != null)
{
//獲取后更新用戶控件標(biāo)志
res = true;
taskCtrl.Tag = res;
//定義上次顯示的行數(shù)
int lastshow = 0;
for (int row = taskCtrl.Startrecord; row < taskCtrl.Endrecord; row++)
{
//拼接循環(huán)的字符串
str = str + "<record>" + row + "</record>";
//防止每條都更新UI造成阻塞,設(shè)置每1000條更新一次UI
if (row - lastshow > 1000 || row == taskCtrl.Endrecord)
{
this.BeginInvoke(TaskProcessbar, taskCtrl, row);
lastshow = row;
}
}
this.BeginInvoke(TaskProcessbar, taskCtrl, taskCtrl.Endrecord);
//將循環(huán)完后拼接好的字符串保存到txt文件里,文件名為用戶控件名稱
string filePath = Directory.GetCurrentDirectory() + "\\file" + taskCtrl.Name + ".txt";
System.IO.File.WriteAllText(filePath, str, Encoding.UTF8);
}
return res;
}
接下來我們運行看看效果
任務(wù)執(zhí)行完后程序目錄下生成對應(yīng)的txt文件
感謝各位的閱讀,以上就是“C#多線程Task的使用”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對C#多線程Task的使用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。