C#知識系列
這篇大概要講一下Task幾個用法,Task建立的方式如下
- task.Start
- Task.Factory.StartNew
- Task.Run
static void Main(string[] args)
{
Task task1 = new Task(() => {
Thread.Sleep(1000);
Console.WriteLine($"hello,task1執行緒ID:{Thread.CurrentThread.ManagedThreadId}");
});
task1.Start();
Task task2 = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
Console.WriteLine($"hello,task2執行緒ID:{Thread.CurrentThread.ManagedThreadId}");
});
Task task3 = Task.Run(() =>
{
Thread.Sleep(1000);
Console.WriteLine($"hello,task3執行緒ID:{Thread.CurrentThread.ManagedThreadId}");
});
Console.WriteLine("主執行緒 run ing");
Console.ReadKey();
}
看到上方範例,會是先執行主執行緒,再去執行其他的執行緒,從這範例來看,並不會阻塞主執行緒
接者來看另一個範例
private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine($"****************button1_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
Task.Run(() => this.DoSomethingLong("btnTask_Click1"));
Task.Run(() => this.DoSomethingLong("btnTask_Click2"));
TaskFactory taskFactory = Task.Factory;
taskFactory.StartNew(() => this.DoSomethingLong("btnTask_Click3"));
new Task(() => this.DoSomethingLong("btnTask_Click4")).Start();
Console.WriteLine($"****************button1_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
private void DoSomethingLong(string name)
{
Console.WriteLine($"****************DoSomethingLong Start {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
long lResult = 0;
//修改前100000000 1000000000
for (int i = 0; i < 1000; i++)
{
lResult += i;
}
Thread.Sleep(2000);
Console.WriteLine($"****************DoSomethingLong End {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}
關於Task.Run VS Task.Factory.StartNew差異
4.0的時候支援Task.Factory.StartNew,4.5支援Task.Run,Factory.StartNew的Method其實提供很多
Method,若執行緒長時間運作則採用Task.Factory.StartNew,剩下就使用Task.Run吧
我們來用一個比較明顯的例子,把例子改寫一下
private void Coding(string name, string projectName)
{
Console.WriteLine($"***Coding Start {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***");
long lResult = 0;
for (int i = 0; i < 1000000000; i++)
{
lResult += i;
}
Console.WriteLine($"***Coding End {name} {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***");
}
private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine($"****************button1_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
List<Task> taskList = new List<Task>();
Console.WriteLine($"專案經理啟動一個項目...【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
Console.WriteLine($"前置作業...【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
Console.WriteLine($"開始Coding...【{ Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
//非同步 沒有阻塞
taskList.Add(Task.Run(() => this.Coding("李一", "Clinent")));
taskList.Add(Task.Run(() => this.Coding("李二", "Portal")));
taskList.Add(Task.Run(() => this.Coding("李三", "Service")));
taskList.Add(Task.Run(() => this.Coding("李四", "Jump")));
taskList.Add(Task.Run(() => this.Coding("李五", "Monitor")));
Console.WriteLine("告訴甲方驗收,上限使用");
Console.WriteLine($"****************button1_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
怪了...我還沒開始做,怎麼能驗收完畢,案子結束阿...
接者我們加入Task.WaitAll
Task.WaitAll:它會阻塞目前執行緒,等待處理完成之後,才能進入下一行,這個部分會卡介面。
private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine($"****************button1_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
List<Task> taskList = new List<Task>();
Console.WriteLine($"專案經理啟動一個項目...【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
Console.WriteLine($"前置作業...【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
Console.WriteLine($"開始Coding...【{ Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
taskList.Add(Task.Run(() => this.Coding("李一", "Clinent")));
taskList.Add(Task.Run(() => this.Coding("李二", "Portal")));
taskList.Add(Task.Run(() => this.Coding("李三", "Service")));
taskList.Add(Task.Run(() => this.Coding("李四", "Jump")));
taskList.Add(Task.Run(() => this.Coding("李五", "Monitor")));
//加入WaitAll
Task.WaitAll(taskList.ToArray()); //會阻塞目前執行緒,等待全部完成後,才能進入下一行,卡界面
Console.WriteLine("告訴甲方驗收,上限使用");
Console.WriteLine($"****************button1_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
WaitAll也有提供限時等待的方法,這樣才不會一直等下去
Task.WaitAll(taskList.ToArray(),1000);
WaitAll使用方式:使用者查詢有多個資料來源 ,例如:首頁→可以多執行緒開發,提升查詢效能,要拿到全部資料後才能返回 WaitAll
首先我們從上面來探討,我們再用多執行緒,首先任務能夠分拆
什麼時候使用多執行緒?
任務要能夠併發執行,提升速度,優化體驗,以CPU資源換取時間
初學者使用上的問題遇到盲點的問題
1.假設一萬筆資料,這一百筆資料 建構一個執行緒,共一百個執行緒 寫入DB ,寫入DB ,因為DB只有一個,DB忙翻天,這並無助提升效能
2.1+2...+100 用執行緒...這也必無助提升效能...
接者加上WaitAny
WaitAny:會阻塞目前執行緒,等某個任務完成後,才能進入下一行,卡界面
private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine($"****************button1_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
//首先任務能分拆
List<Task> taskList = new List<Task>();
Console.WriteLine($"專案經理啟動一個項目...【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
Console.WriteLine($"前置作業...【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
Console.WriteLine($"開始Coding...【{ Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
//非同步 沒有阻塞
taskList.Add(Task.Run(() => this.Coding("李一", "Clinent")));
taskList.Add(Task.Run(() => this.Coding("李二", "Portal")));
taskList.Add(Task.Run(() => this.Coding("李三", "Service")));
taskList.Add(Task.Run(() => this.Coding("李四", "Jump")));
taskList.Add(Task.Run(() => this.Coding("李五", "Monitor")));
Task.WaitAny(taskList.ToArray()); //會阻塞目前執行緒,等某個任務完成後,才能進入下一行,卡界面
Console.WriteLine($"完成里程碑...【{ Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
Task.WaitAll(taskList.ToArray()); //會阻塞目前執行緒,等待全部完成後,才能進入下一行,卡界面
Console.WriteLine("告訴甲方驗收,上限使用");
Console.WriteLine($"****************button1_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
WaitAny也有提供限時等待的方法,這樣才不會一直等下去
Task.WaitAny(taskList.ToArray(),1000); //限時等待
WaitAny使用方式:一個商品查詢有多個資料來源 商品查詢→多個資料來源→多執行緒開發,只需要一個結果即可,就可採用
反過來另外兩個WhenAny和WhenAll是不阻塞的用法,不卡介面,比對一下WaitAny和WaitAll
private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine($"****************button1_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
//首先任務能分拆
List<Task> taskList = new List<Task>();
Console.WriteLine($"專案經理啟動一個項目...【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
Console.WriteLine($"前置作業...【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
Console.WriteLine($"開始Coding...【{ Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
taskList.Add(Task.Run(() => this.Coding("李一", "Clinent")));
taskList.Add(Task.Run(() => this.Coding("李二", "Portal")));
taskList.Add(Task.Run(() => this.Coding("李三", "Service")));
taskList.Add(Task.Run(() => this.Coding("李四", "Jump")));
taskList.Add(Task.Run(() => this.Coding("李五", "Monitor")));
Task.WhenAny(taskList.ToArray()).ContinueWith(t => {
Console.WriteLine($"第一個完成,該獎勵...{Thread.CurrentThread.ManagedThreadId.ToString("00")} ");
});
Task.WhenAll(taskList.ToArray()).ContinueWith(t => {
Console.WriteLine($"部屬環境,整合測試...{Thread.CurrentThread.ManagedThreadId.ToString("00")} ");
});
//Console.WriteLine($"****************button1_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
第二種用法TaskFactory,跟上方也是大同小異,不阻塞呼叫taskfactory.ContinueWhenAny、taskfactory.ContinueWhenAll
private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine($"****************button1_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
//首先任務能分拆
List<Task> taskList = new List<Task>();
Console.WriteLine($"專案經理啟動一個項目...【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
Console.WriteLine($"前置作業...【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
Console.WriteLine($"開始Coding...【{ Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
//非同步 沒有阻塞
taskList.Add(Task.Run(() => this.Coding("李一", "Clinent")));
taskList.Add(Task.Run(() => this.Coding("李二", "Portal")));
taskList.Add(Task.Run(() => this.Coding("李三", "Service")));
taskList.Add(Task.Run(() => this.Coding("李四", "Jump")));
taskList.Add(Task.Run(() => this.Coding("李五", "Monitor")));
//Task.WhenAny(taskList.ToArray()).ContinueWith(t => {
// Console.WriteLine($"第一個完成...{Thread.CurrentThread.ManagedThreadId.ToString("00")} ");
//});
//Task.WhenAll(taskList.ToArray()).ContinueWith(t => {
// Console.WriteLine($"部屬環境,整合測試...{Thread.CurrentThread.ManagedThreadId.ToString("00")} ");
//});
//第二種用法
TaskFactory taskfactory = new TaskFactory();
taskfactory.ContinueWhenAny(taskList.ToArray(), tList => {
Console.WriteLine($"第一個完成...{Thread.CurrentThread.ManagedThreadId.ToString("00")} ");
});
taskfactory.ContinueWhenAll(taskList.ToArray(), tList => {
Console.WriteLine($"部屬環境,整合測試...{Thread.CurrentThread.ManagedThreadId.ToString("00")} ");
});
Console.WriteLine($"****************button1_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
元哥的筆記