日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

程序员修仙之路--设计一个实用的线程池

發布時間:2023/12/4 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 程序员修仙之路--设计一个实用的线程池 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

菜菜呀,我最近研究技術呢,發現線上一個任務程序線程數有點多呀

CEO,CTO,CFO于一身的CXO

x總,你學編程呢?

菜菜

作為公司總負責人,我以后還要管理技術部門呢,怎么能不會技術呢

CEO,CTO,CFO于一身的CXO

(技術部完了)。。。。。。。

菜菜

趕緊看看線上那個線程特別多的程序,給你2個小時優化一下

CEO,CTO,CFO于一身的CXO

x總,我想辭職

菜菜

菜菜呀,心不要浮躁,學學小馬,心平氣和養養生

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?QueueArray<T>
????{

????????//隊列元素的數組容器
????????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?QueueLinkList<T>
????{

????????LinkedList<T>?contianer?=?null;
????????public?QueueLinkList()
????????
{
????????????contianer?=?new?LinkedList<T>();
????????}
????????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. 當隊列用鏈表方式實現的時候,由于鏈表的首尾操作時間復雜度都是O(1),而且沒有空間大小的限制,所以一般的隊列用鏈表實現更簡單

5. 當隊列中無元素可出隊或者沒有空間可入隊的時候,是阻塞當前的操作還是返回錯誤信息,取決于在座各位隊列的設計者了。



◆◆簡單實用的線程池◆◆


Net Core C# 版本


//線程池
????public?class?ThreadPool
????{
????????bool?PoolEnable?=?false;?//線程池是否可用?
????????List<Thread>?ThreadContainer?=?null;?//線程的容器
????????ConcurrentQueue<ActionData>?JobContainer?=?null;?//任務的容器
????????public?ThreadPool(int?threadNumber)
????????
{
????????????PoolEnable?=?true;
????????????ThreadContainer?=?new?List<Thread>(threadNumber);
????????????JobContainer?=?new?ConcurrentQueue<ActionData>();
????????????for?(int?i?=?0;?i?<?threadNumber;?i++)
????????????{
????????????????var?t?=?new?Thread(RunJob);
????????????????ThreadContainer.Add(t);
????????????????t.Start();
????????????}???????????
????????}
????????//向線程池添加一個任務
????????public?void?AddTask(Action<object>?job,object?obj,?Action<Exception>?errorCallBack=null)
????????
{
????????????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?job=null;
????????????????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?Action<object>?Job?{?get;?set;?}
????????//發生異常時候的回調方法
????????public?Action<Exception>?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讓我做一個計算器●程序猿修仙之路--數據結構之設計高性能訪客記錄系統程序猿修仙之路--算法之快速排序到底有多快程序猿修仙之路--數據結構之你是否真的懂數組?

●程序猿修仙之路--算法之希爾排序!

●程序員修仙之路--算法之插入排序!

●程序員修仙之路--算法之選擇排序!

互聯網之路,菜菜與君一同成長

? ? 長按識別二維碼關注

你點的每個贊,我都認真當成了喜歡


總結

以上是生活随笔為你收集整理的程序员修仙之路--设计一个实用的线程池的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。