c# 線程使用總結
C# 里有好幾種線程,每種有不同的實現方式,對應實現不同的功能,做個總結方便以后使用。
1. 委托
(1) 同步委托開啟線程
不需要傳參:
Action action1 = new Action(Count);// count 是一個計數函數 action1.Invoke();
需要傳參:
Action<string> action2 = new Action<string>(Count);// Count是一個帶有object參數的計數函數 action2.Invoke("線程開啟");
總結:同步委托可以實現傳參與不傳參兩種情況,但使用以后,界面卡頓,大多數情況下不宜使用
(2)異步委托開啟線程
傳遞參數:
Action<string> action3 = new Action<string>(Count);// Count是一個帶有object參數的計數函數 action3.BeginInvoke("線程開啟", null, null);
傳遞參數并等待線程結束,傳回線程運行之后的返回值:
private delegate int CallBack(string value);//帶有返回值 int count = 0; private int Back(string str) { Stopwatch sp = new Stopwatch(); sp.Start(); for (int i = 10; i > 0; i--) { System.TimeSpan startTime = sp.Elapsed; while ((sp.Elapsed - startTime).TotalMilliseconds <= 1000) { System.Threading.Thread.Sleep(1); } TextChange(textBox1, str); TextChange(textBox2, i.ToString()); count++; } return count; } CallBack action = new CallBack(Back); IAsyncResult cookie = action.BeginInvoke("線程開啟", null, null); int return= action.EndInvoke(cookie);
總結:如果只傳遞參數,異步線程不會卡頓,但如果需要等待線程結束 ,用到 action.EndInvoke,界面會卡頓,
這時外加上一個new Thread或者其他線程,可以解決卡頓問題,這個后面會說到。
(3) 通過thread開啟線程
傳遞參數:
Thread thread = new Thread(new ParameterizedThreadStart(Count));// Count是一個帶有object參數的計數函數 thread.Start("線程開啟"); //Thread.Sleep(1000); //thread.Abort();//線程終止,立馬停下,方法停止 //Console.WriteLine(thread.ThreadState);
或者用一個更簡單的方式:
new Thread((value)=> { //這里直接寫Count函數的內容 }).Start("線程開啟");
不傳遞參數:
Thread thread = new Thread(new ThreadStart(Count));// Count是不帶參數的計數函數 thread.Start(); //Thread.Sleep(1000); //thread.Abort();//線程終止,立馬停下,方法停止 //Console.WriteLine(thread.ThreadState);
或者(這里演示用new thread解決action.EndInvoke卡頓問題):
new Thread(()=> { CallBack action = new CallBack(Back); IAsyncResult cookie = action.BeginInvoke("線程開啟", null, null); int return= action.EndInvoke(cookie); int count = 0; private int Back(string str) { Stopwatch sp = new Stopwatch(); sp.Start(); for (int i = 10; i > 0; i--) { System.TimeSpan startTime = sp.Elapsed; while ((sp.Elapsed - startTime).TotalMilliseconds <= 1000) { System.Threading.Thread.Sleep(1); } TextChange(textBox1, str); TextChange(textBox2, i.ToString()); count++; } return count; } }).Start();
(4) timer開啟線程
可以傳遞參數并循環執行:
System.Threading.Timer timer = new System.Threading.Timer(new TimerCallback(Count), "線程開啟", Timeout.Infinite, Timeout.Infinite);// Count是一個帶有object參數的計數函數 timer.Change(0, 0); //開啟timer //Thread.Sleep(100); //timer.Change(-1, -1);//關閉timer,如果count函數沒有執行完,不會停下,直到count函數執行完畢
總結:如果不需要傳參,把“線程開啟”換成null即可。
timer.Change(0,0) 中第一個0的位置表示開啟時間(單位ms),0表示立馬執行,-1表示永遠不執行,第二個0的位置表示循環周期(單位ms),0或者-1表示不循環,其他正整數表示循環周期。
(5) Task開啟線程
不傳遞參數(快速啟動方式):
Task.Run(() => Count());//Count是一個計時函數
傳遞參數:
Task task = new Task(new Action<object>(Count), "線程開啟", cts.Token);//無返回值的傳參 task.Start(); //或者 Task.Factory.StartNew(new Action<object>(Count), "線程開啟");
等待單個線程結束并返回結果:
int count = 0; private int Test2(object obj) { Stopwatch sp = new Stopwatch(); sp.Start(); for (int i = 5; i > 0; i--) { System.TimeSpan startTime = sp.Elapsed; while ((sp.Elapsed - startTime).TotalMilliseconds <= 1000) { System.Threading.Thread.Sleep(1); } TextChange(textBox2, i.ToString()); TextChange(textBox1, obj.ToString()); count++; } return count; } Func<object, int> func = new Func<object, int>(Test2); Task<int> task1 = new Task<int>(func, "線程開啟"); task1.Start(); //if(task1.Result ==5) //直接寫等待并判斷會卡界面 //{ // TextChange(textBox6, task1.Result.ToString()); //} //開啟另一個task2等待task1結束 Task task2 = task1.ContinueWith(reValue => { if (reValue.Result == 5) { TextChange(textBox6, reValue.Result.ToString()); } else { return; } });
等待多個線程全部運行結束,并返回結果 :
int count = 0; private int Test2(object obj) { Stopwatch sp = new Stopwatch(); sp.Start(); for (int i = 5; i > 0; i--) { System.TimeSpan startTime = sp.Elapsed; while ((sp.Elapsed - startTime).TotalMilliseconds <= 1000) { System.Threading.Thread.Sleep(1); } TextChange(textBox2, i.ToString()); TextChange(textBox1, obj.ToString()); count++; } return count; } List<int> resulesList = new List<int>(); List<Task<int>> taskList = new List<Task<int>>(); for (int i = 0; i < 5; i++) { taskList.Add(Task<int>.Factory.StartNew(new Func<object, int>(test2), i)); } Task.Factory.ContinueWhenAll(taskList.ToArray(), t => //通過回調完成主線程任務,ContinueWhenAll創建一組任務,該任務在指定的一組任務完成后開始 { for (int i = 0; i < 5; i++) { resulesList.Add(taskList[i].Result); } });
等待多個線程任一運行結束,并返回結果 :
Task.Factory.ContinueWhenAny
總結:Task線程的建立在一定程度上依賴于委托,通常,不需要返回值用Action,需要返回值用Func,也可以自建委托,
Task可以等待單一線程結束/多個線程結束/任一線程結束,分別對應不同的方法
(6) async / await 異步等待
沒有返回值的等待:
private async void Waiting() { var t = Task.Run(() => { Thread.Sleep(5000); }); await t;
Console.WriteLine("等待結束"); }
等待返回值:
private async void Waiting() { var t = Task.Run(() => { Thread.Sleep(5000); return "線程結束"; }); textBox2.Text = await t; }
總結:這種方式可以等待結果,界面不卡頓,且跨線程修改控件屬性問題也可以避免

浙公網安備 33010602011771號