小学生做创客大赛网站的题,网站建设的完整流程,成都公司注册网,国际羽联最新排名菜菜呀#xff0c;我最近研究技术呢#xff0c;发现线上一个任务程序线程数有点多呀CEO,CTO,CFO于一身的CXOx总#xff0c;你学编程呢#xff1f;菜菜作为公司总负责人#xff0c;我以后还要管理技术部门呢#xff0c;怎么能不会技术呢CEO,CTO,CFO于一身的CXO#xff08… 菜菜呀我最近研究技术呢发现线上一个任务程序线程数有点多呀CEO,CTO,CFO于一身的CXOx总你学编程呢菜菜作为公司总负责人我以后还要管理技术部门呢怎么能不会技术呢CEO,CTO,CFO于一身的CXO技术部完了。。。。。。。菜菜赶紧看看线上那个线程特别多的程序给你2个小时优化一下CEO,CTO,CFO于一身的CXOx总我想辞职菜菜菜菜呀心不要浮躁学学小马心平气和养养生CEO,CTO,CFO于一身的CXO............................菜菜好了给你半天时间把线程多的问题优化一下要不然扣你绩效CEO,CTO,CFO于一身的CXO嘞了个擦。。。。。。菜菜◆◆原因排查◆◆ 经过一个多小时的代码排查终于查明了线上程序线程数过多的原因这是一个接收mq消息的一个服务程序大体思路是这样的监听的线程每次收到一条消息就启动一个线程去执行每次启动的线程都是新的。说到这里咱们就谈一谈这个程序有哪些弊端呢1. 每次收到一条消息都创建一个新的线程要知道线程的资源对于系统来说是很昂贵的消息处理完成还要销毁这个线程。2. 这个程序用到的线程数量是没有限制的。当线程到达一定数量程序反而因线程在cpu切换开销的原因处理效率降低。无论的你的服务器cpu是多少核心这个现象都有发生的可能。◆◆解决问题◆◆ 线程多的问题该怎么解决呢增加cpu核心数治标不治本。对于开发者而言最为常用也最为有效的是线程池化也就是说线程池。 线程池是一种多线程处理形式处理过程中将任务添加到队列然后在创建线程后自动启动这些任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。 例如线程数一般取cpu数量2比较合适线程数过多会导致额外的线程切换开销。 线程池其中一项很重要的技术点就是任务的队列队列虽然属于一种基础的数据结构但是发挥了举足轻重的作用。◆◆队列◆◆ 队列是一种特殊的线性表特殊之处在于它只允许在表的前端front进行删除操作而在表的后端rear进行插入操作和栈一样队列是一种操作受限制的线性表。进行插入操作的端称为队尾进行删除操作的端称为队头。 队列是一种采用的FIFO(first in first out)方式的线性表也就是经常说的先进先出策略。实现数组 队列可以用数组Q[1…m]来存储数组的上界m即是队列所容许的最大容量。在队列的运算中需设两个指针head队头指针指向实际队头元素1的位置tail队尾指针指向实际队尾元素位置。一般情况下两个指针的初值设为0这时队列为空没有元素。以下为一个简单的实例生产环境需要优化public class QueueArrayT { //队列元素的数组容器 T[] container null; int IndexHeader, IndexTail; public QueueArray(int size) { container new T[size]; IndexHeader 0; IndexTail 0; } public void Enqueue(T item) { //入队的元素放在头指针的指向位置然后头指针前移 container[IndexHeader] item; IndexHeader; } public T Dequeue() { //出队把尾元素指针指向的元素取出并清空不清空也可以对应的位置尾指针前移 T item container[IndexTail]; container[IndexTail] default(T); IndexTail; return item; } }链表 队列采用的FIFO(first in first out)新元素总是被插入到链表的尾部而读取的时候总是从链表的头部开始读取。每次读取一个元素释放一个元素。所谓的动态创建动态释放。因而也不存在溢出等问题。由于链表由元素连接而成遍历也方便。以下是一个实例仅供参考public class QueueLinkListT { LinkedListT contianer null; public QueueLinkList() { contianer new LinkedListT(); } public void Enqueue(T item) { //入队的元素其实就是加入到队尾 contianer.AddLast(item); } public T Dequeue() { //出队取链表第一个元素然后把这个元素删除 T item contianer.First.Value; contianer.RemoveFirst(); return item; } }队列的扩展阅读1. 队列通过数组来实现的话有什么问题吗是的。首先基于数组不可变本质的因素具体可参考菜菜之前的文章当一个队列的元素把数组沾满的时候数组扩容是有性能问题的数组的扩容过程不只是开辟新空间分配内存那么简单还要有数组元素的copy过程更可怕的是会给GC造成极大的压力。如果数组比较小可能影响比较小但是当一个数组比较大的时候比如占用500M内存的一个数组数据copy其实会造成比较大的性能损失。2. 队列通过数组来实现随着头指针和尾指针的位置移动尾指针最终会指向第一个元素的位置也就是说没有元素可以出队了其实要解决这个问题有两种方式其一在出队或者入队的过程中不断的移动所有元素的位置避免上边所说的极端情况发生其二可以把数组的首尾元素连接起来使其成为一个环状也就是经常说的循环队列。3. 队列在一些特殊场景下其实还有一些变种比如说循环队列阻塞队列并发队列等有兴趣的同学可以去研究一下这里不在展开讨论。这里说到阻塞队列就多说一句其实用阻塞队列可以实现一个最基本的生产者消费者模式。4. 当队列用链表方式实现的时候由于链表的首尾操作时间复杂度都是O1而且没有空间大小的限制所以一般的队列用链表实现更简单5. 当队列中无元素可出队或者没有空间可入队的时候是阻塞当前的操作还是返回错误信息取决于在座各位队列的设计者了。◆◆简单实用的线程池◆◆Net Core C# 版本//线程池 public class ThreadPool { bool PoolEnable false; //线程池是否可用 ListThread ThreadContainer null; //线程的容器 ConcurrentQueueActionData JobContainer null; //任务的容器 public ThreadPool(int threadNumber) { PoolEnable true; ThreadContainer new ListThread(threadNumber); JobContainer new ConcurrentQueueActionData(); for (int i 0; i threadNumber; i) { var t new Thread(RunJob); ThreadContainer.Add(t); t.Start(); } } //向线程池添加一个任务 public void AddTask(Actionobject job,object obj, ActionException errorCallBacknull) { if (JobContainer ! null) { JobContainer.Enqueue(new ActionData { Job job, Data obj , ErrorCallBack errorCallBack }); } } //终止线程池 public void FinalPool() { PoolEnable false; JobContainer null; if (ThreadContainer ! null) { foreach (var t in ThreadContainer) { //强制线程退出并不好会有异常 //t.Abort(); t.Join(); } ThreadContainer null; } } private void RunJob() { while (true JobContainer!null PoolEnable) { //任务列表取任务 ActionData jobnull; JobContainer?.TryDequeue(out job); if (job null) { //如果没有任务则休眠 Thread.Sleep(10); continue; } try { //执行任务 job.Job.Invoke(job.Data); } catch(Exception error) { //异常回调 job?.ErrorCallBack(error); } } } } public class ActionData { //执行任务的参数 public object Data { get; set; } //执行的任务 public Actionobject Job { get; set; } //发生异常时候的回调方法 public ActionException ErrorCallBack { get; set; } }使用方法ThreadPool pool new ThreadPool(100); for (int i 0; i 5000; i) { pool.AddTask((obj) { Console.WriteLine(${obj}__{System.Threading.Thread.CurrentThread.ManagedThreadId}); }, i, (e) { Console.WriteLine(e.Message); }); } pool.FinalPool(); Console.Read();●程序员修仙之路--数据结构之CXO让我做一个计算器●程序猿修仙之路--数据结构之设计高性能访客记录系统●程序猿修仙之路--算法之快速排序到底有多快●程序猿修仙之路--数据结构之你是否真的懂数组●程序猿修仙之路--算法之希尔排序●程序员修仙之路--算法之插入排序●程序员修仙之路--算法之选择排序互联网之路菜菜与君一同成长 长按识别二维码关注你点的每个赞我都认真当成了喜欢