将多个线程在线程池中运行
知识点保温
引用一下微软官网的介绍:
线程池: https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.threadpool?redirectedfrom=MSDN&view=netframework-4.7.2
直接上Demo
多线程运行 VS 单线程运行
////// 高效多线程测试 /// public class ThreadTestController : ApiController { static AutoResetEvent autoResetEvent = new AutoResetEvent(false); private static string keySet = System.Configuration.ConfigurationManager.AppSettings["KeySetting"]; /// /// 多线程运行 -- 线程池 /// /// public ApiReponseBaseModel MultThreads() { ApiReponseBaseModel result = new ApiReponseBaseModel(); List<string> list = new List<string>() { "一", "二", "三", "四", "", "五", "六", "七" }; ApiReponseBaseModel masterVal = new ApiReponseBaseModel() { Id = 1, Success = false, Message = "one" }; List<string> newList = new List<string>(); try { int i = 0; foreach (var item in list) { try { if (item == "四") throw new Exception("人造异常"); ThreadPool.QueueUserWorkItem(new WaitCallback(delegate (object obj) { if (string.IsNullOrEmpty(item)) { i++; newList.Add((i).ToString() + ":" + "为空了"); return; } //保证每个子线程都有执行 i++ try { #region ApiReponseBaseModel val = new ApiReponseBaseModel { Id = masterVal.Id, Success = masterVal.Success, Message = masterVal.Message }; #endregion val.Message = item; Thread.Sleep(500); lock (newList) { newList.Add(i.ToString() + ":" + val.Message); } } catch (Exception ex) { newList.Add((i+1).ToString() + ":" + ex.Message); } finally { if (++i == list.Count) autoResetEvent.Set(); } }), null); } catch (Exception ex) { i++; //发生异常,一定要记得 i++ newList.Add(i.ToString() + ":" + ex.Message); continue; } //finally //【误区】千千万万不要在这里写逻辑判断(i == list.Count 或者写 i++),这个finally是主线程的,不一定会在子线程之后执行的 //{ // i++; // if (i == list.Count) autoResetEvent.Set(); //} } autoResetEvent.WaitOne(); newList.Add("the end"); result.Data = newList; } catch (Exception ex) { throw ex; } return result; } /// /// 单线程运行 /// /// public ApiReponseBaseModel SingleThread() { ApiReponseBaseModel result = new ApiReponseBaseModel(); List<string> list = new List<string>() { "一", "二", "三", "四", "", "五", "六", "七" }; ApiReponseBaseModel masterVal = new ApiReponseBaseModel() { Id = 1, Success = false, Message = "one" }; List<string> newList = new List<string>(); int i = 0; foreach (var item in list) { if (string.IsNullOrEmpty(item)) { i++; newList.Add((i).ToString() + ":" + "为空了"); continue; } #region ApiReponseBaseModel val = new ApiReponseBaseModel { Id = masterVal.Id, Success = masterVal.Success, Message = masterVal.Message }; #endregion val.Message = item; Thread.Sleep(500); newList.Add(i.ToString() + ":" + val.Message); i++; } newList.Add("the end"); result.Data = newList; return result; } }
结果:
解析:
1、以上两个方法的运行,我们都在方法中休眠了,用以突显出多线程异步执行达到运行更快的效果。
2、在第一个方法(多线程运行 -线程池)中,用到了线程池,其关键在于 AutoResetEvent 的使用,它的.Set() 和 WaitOne() 是关键点。
WaitOne() 会阻塞当前的线程,而等当前 WaitHandle 发来的信息,Set() 方法就是发出信号的。
3、那么很重要的地方来了,就是怎么在逻辑中表达,所需要计算的数据和执行的进程都已经执行完了,而在恰当的地方适当的时候发起信号,激活主线程。
4、以上多线程方法中,我们还用到了个 lock 锁,这也是极其微妙关键的。我们的多个子线程运行的程序模块中,都有访问了一个共同的对象 newList。newList 是主线程的一个 引用类型变量,为了防止多个线程在同一个时间点对这个变量进行更改操作(Add),
而引发的异常错误,我们在做这类操作的时候,给这个共同的资源(newList)加上锁,让这样的操作一个接着一个来。
进程中的 val 这个变量,变量类型是(ApiReponseBaseModel),也是个引用类型。我们不在子进程中直接用主线程的 masterVal 变量,也是这个原因,防止多个子线程同时对共同资源做修改操作。
5、WaitHandler 类的小图解
我们再试个任务工厂的Demo
知识点保温
任务工厂的知识保温链接:https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskfactory?view=netframework-4.7.2
需要温习任务工厂的小伙伴们可以进入以上链接,吸食日夜精华
直接上菜
////// 多线程运行 -- 任务工厂 /// /// public ApiReponseBaseModel TaskFactory() { ApiReponseBaseModel result = new ApiReponseBaseModel(); List<string> list = new List<string>() { "一", "二", "三", "四", "", "五", "六", "七" }; ApiReponseBaseModel masterVal = new ApiReponseBaseModel() { Id = 1, Success = false, Message = "one" }; List<string> newList = new List<string>(); List taskList = new List (); int i = 0; list.ForEach(p => { taskList.Add(Task.Factory.StartNew(() => { if (string.IsNullOrEmpty(p)) { i++; newList.Add((i).ToString() + ":" + "为空了"); return; } //保证每个子线程都有执行 i++ try { if (p == "四") throw new Exception("人造异常"); #region ApiReponseBaseModel val = new ApiReponseBaseModel { Id = masterVal.Id, Success = masterVal.Success, Message = masterVal.Message }; #endregion val.Message = p; Thread.Sleep(500); lock (newList) { newList.Add(i.ToString() + ":" + val.Message); } } catch (Exception ex) { newList.Add((i + 1).ToString() + ":" + ex.Message); } })); //taskList.Add(Task.Factory.StartNew(fn => //{ //}, null)); }); Task finalTask = Task.Factory.ContinueWhenAll(taskList.ToArray(), fn => { newList.Add("finallyTask the end"); }); finalTask.Wait(); newList.Add("main thread the end"); result.Data = newList; return result; }
效果:
解析:
1、我们在任务工厂里边搞了几个任务,每个任务我们都休眠,模仿长时间运行效果。
2、任务工厂的关键在于 ContinueWhenAll() 方法。顾名思义,当该方法参数所指的任务都执行的时候,执行这一方法,这个方法返回一个任务,我们定义一个任务变量(finalTask)来接收。
紧接着,我们放这个变量(finalTask)进行等待,finalTask.Wait()。
小总结
通过以上三个方法的比较
1、多线程比单线程是有优势的,通常情况下运行时间是会更快的,除非服务器爆了
2、在易于编写使用的角度来说,个人觉得任务工厂的办法会比线程池的方法好一些。
3、多读书,多敲码。 ----- 致爱码仕 & 牧码人
author:韦小明
本文路径:http://www.cnblogs.com/youler/p/9845473.html