C# 中的多线程实现方式
目錄
- 前言:
- 一、使用委托開啟多線程
- 二、使用Thread開啟多線程
- 三、使用ThreadPool開啟多線程
- 四、使用Task開啟多線程
- 五、使用Parallel開啟多線程
前言:
線程 被定義為程序的執(zhí)行路徑。每個(gè)線程都定義了一個(gè)獨(dú)特的控制流。如果您的應(yīng)用程序涉及到復(fù)雜的和耗時(shí)的操作,那么設(shè)置不同的線程執(zhí)行路徑往往是有益的,每個(gè)線程執(zhí)行特定的工作。
下面的 DoSomething 和 Callback 方法將貫穿全文。
private string DoSomething(string name, int millisecondsTimeout){Console.WriteLine($"線程{Thread.CurrentThread.ManagedThreadId:00}執(zhí)行");Thread.Sleep(millisecondsTimeout);return name;}private void Callback() { Console.WriteLine("執(zhí)行回調(diào)。"); }一、使用委托開啟多線程
直接使用委托進(jìn)行異步,.NET Core 已經(jīng)不支持該方法,使用時(shí)將直接拋出錯(cuò)誤。
Func<string, int, string> action = DoSomething;var asyncResult = action.BeginInvoke("JOY", 0, null, null);string result = action.EndInvoke(asyncResult);二、使用Thread開啟多線程
通過擴(kuò)展 Thread 類創(chuàng)建的線程
var thread = new Thread(() => DoSomething("JOY",0));thread.Start();使用 Thread 實(shí)現(xiàn)回調(diào)
private void ThreadWithCallBack(ThreadStart threadStart, Action actionCallBack){var method = new ThreadStart(() =>{threadStart.Invoke();actionCallBack.Invoke();});new Thread(method).Start();}// 調(diào)用時(shí)ThreadWithCallBack(() => DoSomething("JOY", 1000), () => Callback());實(shí)現(xiàn)類型 async/await 的功能,異步,非阻塞,并獲取計(jì)算結(jié)果
private Func<T> ThreadWithReturn<T>(Func<T> func){T result = default;var method = new ThreadStart(() =>{result = func.Invoke();});var thread = new Thread(method);thread.Start();return new Func<T>(() =>{thread.Join();return result;});}// 調(diào)用時(shí)var func = ThreadWithReturn(() => DoSomething("JOY", 1000));var result = func.Invoke();三、使用ThreadPool開啟多線程
將方法假如線程池隊(duì)列以便執(zhí)行
ThreadPool.QueueUserWorkItem(o => DoSomething("JOY", 20));設(shè)置線程池?cái)?shù)量
設(shè)置線程池?cái)?shù)量是全局的
委托異步調(diào)用 Task Parrallel async/await 全部都是線程池的線程
直接使用 Thread 不受這個(gè)數(shù)量限制,(但是會(huì)占用線程池?cái)?shù)量)
使用 ManualResetEvent 等待線程池任務(wù)完成
// 初始設(shè)置為 false 指示任務(wù)是否終止var mre = new ManualResetEvent(false);ThreadPool.QueueUserWorkItem(o =>{DoSomething("JOY", 20);mre.Set(); // 設(shè)置為任務(wù)已終止});mre.WaitOne();四、使用Task開啟多線程
使用Task開啟子線程執(zhí)行任務(wù)
var task_1 = new Task(() => DoSomething("JOY", 2000));task_1.Start();var task_2 = Task.Run(() => DoSomething("JOY", 2000));var taskFactory = Task.Factory;Task task_3 = taskFactory.StartNew(() => DoSomething("JOY", 2000));使用 Delay 方法延遲 Task
Thread.Sleep(2000);// 與 Sleep(2000) 不同的時(shí),Delay(2000) 不會(huì)阻塞當(dāng)前線程,// 可以理解為在子線程內(nèi)部加了一個(gè) Sleep(2000).var task = Task.Delay(2000).ContinueWith(t => DoSomething("JOY", 20));使用 ContinueWith 執(zhí)行回調(diào)
Task.Run(() => DoSomething("JOY", 2000)).ContinueWith(task => Callback());獲取 Task 的返回值,會(huì)阻塞
var task = Task.Run<string>(() => DoSomething("JOY", 1000));string result = task.Result; // 獲取返回值,會(huì)阻塞使用 lock 實(shí)現(xiàn)線程安全
// 在數(shù)據(jù)需要大量的情況下,可以使用數(shù)據(jù)分拆,安全又高效var formLock = new object();var numAsync = 0;for (int i = 0; i < 10000; i++){Task.Run(() =>{// 任意時(shí)刻只有一個(gè)線程能進(jìn)入方法塊lock (formLock) { numAsync++; }});}使用Task等待所有任務(wù)完成,會(huì)阻塞
var taskFactory = Task.Factory;var taskList = new List<Task>();taskList.Add(taskFactory.StartNew(() => DoSomething("JOY_1", 2000)));taskList.Add(taskFactory.StartNew(() => DoSomething("JOY_2", 2000)));taskList.Add(taskFactory.StartNew(() => DoSomething("JOY_3", 2000)));// 阻塞,等待所有任務(wù)都完成Task.WaitAll(taskList.ToArray());等待任務(wù)完成后的回調(diào)
var taskFactory = Task.Factory;var taskList = new List<Task>();taskList.Add(taskFactory.StartNew(() => DoSomething("JOY_1", 2000)));taskList.Add(taskFactory.StartNew(() => DoSomething("JOY_2", 2000)));taskList.Add(taskFactory.StartNew(() => DoSomething("JOY_3", 2000)));// 只要有一個(gè)任務(wù)完成就進(jìn)行的回調(diào), 這里的 task 就是第一個(gè)完成的 TasktaskFactory.ContinueWhenAny(taskList.ToArray(), task => Callback());// 等待所有任務(wù)都完成后進(jìn)的回調(diào),這里的 tasks 就是任務(wù)集taskFactory.ContinueWhenAll(taskList.ToArray(), (tasks) => Callback());控制 Task 的并發(fā)數(shù)量
// 不建議用 ThreadPool.SetMaxThreads 和 ThreadPool.SetMinThreads 直接控制ThreadPool.SetMaxThreads(6, 6);ThreadPool.SetMinThreads(2, 2);// 建議使用Task狀態(tài)控制var tasks = new List<Task>();for (int i = 0; i < 1000; i++){var j = i;if (tasks.Count(task => task.Status != TaskStatus.RanToCompletion) >= 5){Task.WaitAny(tasks.ToArray());tasks = tasks.Where(task => task.Status != TaskStatus.RanToCompletion).ToList();}tasks.Add(Task.Run(() => DoSomething($"JOY_{j + 1}.", 2000)));}使用 Task.WaitAll 方法 可以捕獲多線程異常
try{var tasks = new List<Task>();for (int i = 0; i < 9; i++){var name = $"JOY_{i}";tasks.Add(Task.Run(() =>{if (name.Equals("JOY_0"))throw new Exception("JOY_0異常");else if (name.Equals("JOY_1"))throw new Exception("JOY_1異常");Console.WriteLine($"name: {name}, 成功。");}));}Task.WaitAll(tasks.ToArray());}catch (AggregateException aex){Console.WriteLine($"異常數(shù)量:{aex.InnerExceptions.Count}");}catch (Exception ex){}使用 CancellationToken 取消其他Task或者自生
var cts = new CancellationTokenSource();for (int i = 0; i < 20; i++){var name = $"JOY_{i}";Task.Run(() =>{Console.WriteLine($"任務(wù) {name} 開始。");try{if (name.Equals("JOY_6"))throw new Exception("JOY_6異常");if (cts.IsCancellationRequested)DoSomething(name, 2000);elseConsole.WriteLine($"任務(wù) {name} 取消。");}catch (Exception){cts.Cancel();}// 將CancellationToken作為Task.Run的第二個(gè)參數(shù),// 使得如果線程還沒有啟動(dòng),被取消就不執(zhí)行任務(wù)}, cts.Token);}五、使用Parallel開啟多線程
Parallel 并發(fā)執(zhí)行多個(gè)Action,主線程也會(huì)參與計(jì)算,會(huì)阻塞界面
等于 TaskWaitAll + 主線程計(jì)算
Parallel 的 For 循環(huán)
同樣主線程也會(huì)參與計(jì)算,會(huì)阻塞界面
Parallel 的 ForEach 循環(huán)
同樣主線程也會(huì)參與計(jì)算,會(huì)阻塞界面
Parallel 控制并發(fā)數(shù)量
Parallel.Invoke(options,() => DoSomething("JOY_1", 2000),() => DoSomething("JOY_2", 2000),() => DoSomething("JOY_3", 2000),() => DoSomething("JOY_4", 2000),() => DoSomething("JOY_5", 2000),() => DoSomething("JOY_6", 2000));Parallel.For(0, 9, options, i => DoSomething($"JOY_{i}", 1000));Parallel.ForEach(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },options,i => DoSomething($"JOY_{i}", 2000));總結(jié)
以上是生活随笔為你收集整理的C# 中的多线程实现方式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 绘图:BITMAP和DIB区别
- 下一篇: 使用VS进行SqlServer数据库架构