一、Thread类C#里面的多线程:Thread类是C#语言对线程对象的一个封装。 首先看下如何开启线程,执行委托的内容: /// <summary>/// 一个比较耗时耗资源的私有方法/// </summary>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;for (int i = 0; i < 1_000_000_000; 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}***************"); } /// <summary>/// 多线程 Thread类是.NET Framework 1.0的时候出现的/// Thread:C#对线程对象的一个封装/// </summary>private void btnThread_Click(object sender, EventArgs e) { Console.WriteLine($"****************btnThread_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} " +$"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************"); { ParameterizedThreadStart method = o => this.DoSomethingLong("btnThread_Click"); Thread thread = new Thread(method); thread.Start("浪子天涯");//开启线程,执行委托的内容 } Console.WriteLine($"****************btnThread_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} " +$"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************"); } 线程等待、线程优先级、前台线程和后台线程: { ThreadStart method = () =>{ Thread.Sleep(5000);this.DoSomethingLong("btnThread_Click"); Thread.Sleep(5000); }; Thread thread = new Thread(method); thread.Start(); //开启线程,执行委托的内容//该花括号内的这些方法已经被微软抛弃了,建议不要去用 {//thread.Suspend(); //暂停//thread.Resume();//恢复 真的不该要的,暂停不一定马上暂停;让线程操作太复杂了//thread.Abort();//线程是计算机资源,程序想停下线程,只能向操作系统通知(线程抛异常),//会有延时/不一定能真的停下来//Thread.ResetAbort(); }//1等待while (thread.ThreadState != ThreadState.Stopped) { Thread.Sleep(200); //当前线程休息200ms }//2 Join等待thread.Join(); //运行这句代码的线程,等待thread的完成thread.Join(1000); //最多等待1000msConsole.WriteLine("这里是线程执行完之后才操作。。。");//最高优先级:优先执行,但不代表优先完成看,甚至说极端情况下,还有意外发生,不能通过这个来控制线程的执行先后顺序thread.Priority = ThreadPriority.Highest;//是否是后台线程 默认是falsethread.IsBackground = false; //默认是false 前台线程,进程关闭,线程需要计算完后才退出//thread.IsBackground = true;//关闭进程,线程退出} 下面来看下Thread类的使用: 基于Thread封装一个带有回调的 /// <summary>/// 基于Thread封装一个回调/// 回调:启动子线程执行动作A--不阻塞--A执行完后子线程会执行动作B/// </summary>/// <param name="threadStart">多线程执行的操作</param>/// <param name="actionCallback">线程完成后,回调的动作</param>private void ThreadWithCallBack(ThreadStart threadStart, Action actionCallback) {//Thread thread = new Thread(threadStart);//thread.Start();//thread.Join(); //错了,因为方法被阻塞了//actionCallback.Invoke();ThreadStart method = new ThreadStart(() =>{ threadStart.Invoke(); actionCallback.Invoke(); });new Thread(method).Start(); } { ThreadStart threadStart = () => this.DoSomethingLong("btnThread_Click"); Action actionCallBack = () =>{ Thread.Sleep(2000); Console.WriteLine($"This is Calllback {Thread.CurrentThread.ManagedThreadId.ToString("00")}"); };this.ThreadWithCallBack(threadStart, actionCallBack); } 基于Thread封装一个带有返回值的 /// <summary>/// 基于Thread封装一个带有返回值的/// 1 异步,非阻塞的/// 2 还能获取到最终计算结果/// /// 既要不阻塞,又要计算结果?不可能!故此处返回一个委托,当外部需要使用结果的时候再阻塞,此时可能已经计算完了。/// </summary>private Func<T> ThreadWithReturn<T>(Func<T> func) { T t = default(T); ThreadStart threadStart = new ThreadStart(() =>{ t = func.Invoke(); }); Thread thread = new Thread(threadStart); thread.Start();return new Func<T>(() =>{ thread.Join();//thread.ThreadStatereturn t; }); } { Func<int> func = () =>{ Thread.Sleep(5000);return DateTime.Now.Year; }; Func<int> funcThread = this.ThreadWithReturn(func);//非阻塞Console.WriteLine("do something 1"); Console.WriteLine("do something 2"); Console.WriteLine("do something 3");int iResult = funcThread.Invoke();//阻塞} 控制线程的数量(仅供参考): { List<Thread> threads = new List<Thread>();for (int i = 0; i < 100; i++) {if (threads.Count(t => t.ThreadState == ThreadState.Running) < 10) { Thread thread = new Thread(new ThreadStart(() => { })); thread.Start(); threads.Add(thread); }else{ Thread.Sleep(200); } } } 二、ThreadPool类由于Thread类功能繁多,反而用不好--就像给4岁小孩一把热武器,反而会造成更大的伤害。而且对线程数量也是没有管控的。故微软在.NET Framework 2.0推出来ThreadPool线程池。 如果某个对象创建和销毁代价比较高,同时这个对象还可以反复使用的,就需要一个池子。 保存多个这样的对象,需要用的时候从池子里面获取;用完之后不用销毁,放回池子;(享元模式) 节约资源提升性能;此外,还能管控总数量,防止滥用; ThreadPool的线程都是后台线程。 下面我们直接来看下相关代码: /// <summary>/// ThreadPool线程池/// 由于Thread类功能繁多,反而用不好--就像给4岁小孩一把热武器,反而会造成更大的伤害/// 对线程数量是没有管控的/// /// 线程池是.NET Framework 2.0推出来的/// 如果某个对象创建和销毁代价比较高,同时这个对象还可以反复使用的,就需要一个池子/// 保存多个这样的对象,需要用的时候从池子里面获取;用完之后不用销毁,放回池子;(享元模式)/// 节约资源提升性能;此外,还能管控总数量,防止滥用;/// /// ThreadPool的线程都是后台线程/// /// 大家课后可以试试,基于ThreadPool去封装回调--返回值的/// </summary>private void btnThreadPool_Click(object sender, EventArgs e) { Console.WriteLine($"****************btnThreadPool_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} " +$"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");//启动线程 { ThreadPool.QueueUserWorkItem(o => this.DoSomethingLong("btnThreadPool_Click1")); ThreadPool.QueueUserWorkItem(o => this.DoSomethingLong("btnThreadPool_Click2"), "浪子天涯"); } { ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads); Console.WriteLine($"当前电脑最大workerThreads={workerThreads} 最大completionPortThreads={completionPortThreads}"); ThreadPool.GetMinThreads(out int workerThreadsMin, out int completionPortThreadsMin); Console.WriteLine($"当前电脑最小workerThreads={workerThreadsMin} 最大completionPortThreads={completionPortThreadsMin}");//设置的线程池数量是进程全局的(慎用,一般不用)//委托异步调用--Task--Parrallel--async/await 全部都是线程池的线程//直接new Thread不受这个数量限制的(但是会占用线程池的线程数量)ThreadPool.SetMaxThreads(8, 8); //设置的最大值,必须大于CPU核数,否则设置无效ThreadPool.SetMinThreads(2, 2); Console.WriteLine("====================设置线程池数量最大最小===================="); ThreadPool.GetMaxThreads(out int workerThreads1, out int completionPortThreads1); Console.WriteLine($"当前电脑最大workerThreads={workerThreads1} 最大completionPortThreads={completionPortThreads1}"); ThreadPool.GetMinThreads(out int workerThreadsMin1, out int completionPortThreadsMin1); Console.WriteLine($"当前电脑最大workerThreads={workerThreadsMin1} 最大completionPortThreads={completionPortThreadsMin1}"); }//线程等待 { ManualResetEvent mre = new ManualResetEvent(false);//false---关闭---Set打开---true---WaitOne就能通过//true---打开--ReSet关闭---false--WaitOne就只能等待ThreadPool.QueueUserWorkItem(o =>{this.DoSomethingLong("btnThreadPool_Click1"); mre.Set(); }); Console.WriteLine("Do Something 1"); Console.WriteLine("Do Something 2"); Console.WriteLine("Do Something 3"); mre.WaitOne(); Console.WriteLine("任务已经完成了。。。"); }//写多线程的时候有这么一种说法:不要阻塞线程池里面的线程。//下面是一个死锁的例子 { ThreadPool.SetMaxThreads(8, 8); ManualResetEvent mre = new ManualResetEvent(false);for (int i = 0; i < 10; i++) {int k = i; //此处必须声明一个变量存放i的值,不能直接使用i变量,否则会有问题ThreadPool.QueueUserWorkItem(t =>{ Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId.ToString("00")} show {k}");if (k == 9) //设置了最多只允许8个线程,但此处是9,导致死锁了 { mre.Set(); //开关打开 }else{ mre.WaitOne(); //线程等待,阻塞 } }); }if (mre.WaitOne()) //开关没打开,一直等待,死锁了 { Console.WriteLine("任务全部执行成功!"); } } Console.WriteLine($"****************btnThreadPool_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} " +$"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************"); } Demo源码: 链接:https://pan.baidu.com/s/1wVscaka37emNGz9x-rm0qA 此文由博主精心撰写转载请保留此原文链接:https://www.cnblogs.com/xyh9039/p/13550714.html 版权声明:如有雷同纯属巧合,如有侵权请及时联系本人修改,谢谢!!! |
|