1. 程式人生 > >聊聊多執行緒那一些事兒 之 四 經典應用(取與舍、動態建立)

聊聊多執行緒那一些事兒 之 四 經典應用(取與舍、動態建立)

   hello task,咱們又見面啦!!前面已經通過三篇簡單的文章,對多執行緒的建立、執行、阻塞、等待、取消、延遲操作、非同步方法等相關的知識點,通過這一些介紹,現在上手寫一個多執行緒就是分分鐘的小事件。如果需要看前三排文章的小夥伴,可以點選下面連結快速閱讀謝謝!

    說了那麼多後,我仔細想了一下,還是要來點實際的專案用例比較實在,那麼我現在就講我平時在專案中用常用的一些業務梳理處理,以供參考,寫到不好勿噴,有更好的解決方式,歡迎交流。謝謝!

 

應用一、多執行緒的中的取與舍

 

    還是用上幾篇文章中的關於酒店客房的資料來為例分析,假設系統同時對接了x程、y龍、q哪三家介面資料,使用者進入到某一個酒店預訂頁面,系統需要實時的到第三方取該酒店對應房間的實時動態資料呈現給使用者,但是在這個過程中,不能讓使用者等待的太久,並且能夠儘可能多的提供多渠道給使用者選擇,那麼這個時候該如何去實現這個需求呢?

做過聚合平臺的同學,無論是酒店、機票、諮詢等等,或多或少都會遇到這樣類似的業務場景,下面分享一下,我們平時是怎麼實現。

    簡單的說就是一個取與舍的邏輯,你想啊,不同的介面方,介面的查詢效率不盡相同,並且同一介面在不同時間處理時間也不盡相同,遇到某一些特殊情況,有可能一個介面資料需要等待5-10s甚至更久才能取出資料,這個時候你不可能讓使用者也等待這麼久吧,如果是這樣,估計使用者早嚇跑了。況且這樣等待下去,也會對系統帶來很大的壓力,尤其是在做活動高峰期時,那直接就是系統的一個瓶頸了。

    為了到達這一目的,那麼在實現上,我們首先應該想到的是,在約定時間內未返回資料的介面,那麼我們直接放棄,只展示給使用者已取到的資料即可。那在技術上該如何實現呢?

    在實現上,有了Task,一切都變得那麼輕鬆啦,因為Task.Wait方法已經為我們考慮到了這樣的場景,直接使用即可。

我們看看Task.Wait的幾個過載吧:

    

    看到了吧,在過載方法中,有一個timeOut欄位,該欄位就是用於捨棄超時未處理完成的執行緒任務。那具體該怎麼用呢?直接上程式碼吧!

    

static void Main(string[] args)
{
    Console.WriteLine("開始獲取酒店資料......");
// 獲取最新的客房資訊(假設最大等待時間為1S)
    List<string> listHotelRoomInfro = GetHotelRoomInfro(1000);
    Console.WriteLine("獲取酒店資料獲取完畢,獲取到的資料為:");
foreach (var item in listHotelRoomInfro)
    {
        Console.WriteLine(item);
    }
    Console.ReadLine();
 }
 
/// <summary>
/// 獲取最新的客房資訊
/// </summary>
/// <param name="maxWaitTimes">最大獲取時間(也就是捨棄時間),單位毫秒</param>
/// <returns>客房資訊集合</returns>
private static List<string> GetHotelRoomInfro(int maxWaitTimes)
 {
// 模擬儲存獲取到的酒店客房資料集合
     List<string> listHotelRoomInfro = new List<string>();
// 在此我也分別對3種不同渠道,採用3種不同的方式來實現
// 其一、通過傳統的 new 方式來例項化一個task物件,獲取 攜程 的客房資料
     Task newCtripTask = new Task(() =>
    {
// 具體獲取業務邏輯處理...
         Thread.Sleep(new Random().Next(100, 999));
        listHotelRoomInfro.Add("我是來自 攜程 的最新客房資訊");
    });
// 啟動 tsak
     newCtripTask.Start();
// 其二、通過工廠 factory 來生成一個task物件,並自啟動:獲取 藝龍 的客房資料
     Task factoryElongTask = Task.Factory.StartNew(() =>
     {
// 具體獲取業務邏輯處理...
         Thread.Sleep(new Random().Next(555, 1500));
         listHotelRoomInfro.Add("我是來自 藝龍 的最新客房資訊");
     });
// 其三、通過 Task.Run(Action action) 來建立一個自啟動task:獲取 去哪兒網 的客房資料
     Task runQunarTask = Task.Run(() =>
     {
// 具體獲取業務邏輯處理...
         Thread.Sleep(new Random().Next(1100, 2000));
         listHotelRoomInfro.Add("我是來自 去哪兒網 的最新客房資訊");
     });
// 等待獲取介面,阻塞主流程,如果 在 指定的時間 maxWaitTimes未返回資料的介面方直接捨棄掉
     Task.WaitAll(new Task[] { newCtripTask, factoryElongTask, runQunarTask }, maxWaitTimes);
return listHotelRoomInfro;
 }

 

執行結果:

    通過上面的執行結果,我們發現在1s內,只有攜程返回了資料,那麼最終也就只返回給使用者攜程的資料。這個例項就這樣實現了,當然,wi相信你或許會有更好的實現,歡迎一起交流與學習,謝謝

 

應用二:動態建立多執行緒均批處理

 

    ​在實際專案中,我們會遇到需要根據待處理任務數量,動態建立多執行緒來最優化分批處理。哈哈哈是不是說的有點空洞,雲裡霧裡的,不急不急,下面來一個實際場景,一看你就明明白白啦。

    ​需求:還是以酒店資料同步為例。具體需求是這樣的:

    ​1.手動批選擇指定酒店,單次最多可選擇500個

    ​2.系統需要以最快的方式同步會介面方的最新資料

    ​哈哈哈看了需求是不是覺得很簡單,就兩句話,那該如何來實現這個需求呢?

    ​也許你會說,簡單啊,根據所選擇的酒店以次排隊同步資料就對了嘛!這樣可不行哦,如果使用者選擇了500個酒店,假設每個酒店資料同步需要2秒,那麼500條資料都需要1000秒,那需要接近20分鐘才處理完,這可不是時間最優啊!

    ​那你也要你還會說那就直接每一個酒店都開一個執行緒來處理,這樣速度是不是夠快啦!嗯你說的沒錯,這樣速度是上去了,但是呢,如果使用者選擇500個酒店,那麼就需要建立500個執行緒,也就是500個併發,這樣會有什麼問題呢?第一.你自己的伺服器扛的住嗎?第二.如果你伺服器扛的住,那麼介面允許你這麼幹嘛?估計早都把你拉黑名單了。所以這也不是最優方案。

    ​哈哈,說了那麼多,那麼具體該如何來實現呢?其實也簡單,我想說的方案就去以上兩兩種方案的一個折中方案。動態的根據資源等多因素動態建立合理的執行緒,然後在並行處理。來來,直接上程式碼!

class Program
 {
     /// <summary>
     /// 最多允許建立的執行緒數,可以通過配置檔案來配置
     /// 具體的值也該是由:伺服器資源+介面限制等多因數來確定的
     /// </summary>
     public static int maxThread = 10;
     static void Main(string[] args)
     {
         // 假設選擇處理20條酒店資料
         List<string> listHotel = new List<string>();
         for (int i = 0; i < 20; i++)
         {
             listHotel.Add($"我是酒店{(i + 1).ToString().PadLeft(2, '0')}");
         }

         // 建立Tsak處理酒店資料同步
         AsyncDynamicSynchronouslyHotel(listHotel);

         Console.ReadLine();
     }

     /// <summary>
     /// 根據酒店資料量,動態建立Tsak來處理酒店資料同步
     /// </summary>
     /// <param name="listHotel">待處理的酒店資料列表</param>
     private static void AsyncDynamicSynchronouslyHotel(List<string> listHotel)
     {
         object hotelTaskLock = new object();

         // 先做一個非空判斷
         if (listHotel == null || listHotel.Count < 1)
         {
             return;
         }

         // task執行緒陣列
         List<Task> taskList = new List<Task>();

         // 首先根據資源資料量+最大允許執行緒數來確定需要開啟的執行緒
         int taskCount = listHotel.Count < maxThread ? listHotel.Count : maxThread;

         // 建立指定是task執行緒數
         for (int i = 0; i < taskCount; i++)
         {
             // 建立一個task執行緒
             taskList.Add(new Task(() =>
             {
                 while (listHotel != null && listHotel.Count > 0)
                 {
                     // 給該執行緒分配一個酒店處理任務
                     string hotelInfro = string.Empty;

                     // 執行緒通過,加一個資源鎖
                     lock (hotelTaskLock)
                     {
                         // 在獲取到鎖後,還需要做一個資源判斷,避免獲取到鎖後,資源以及被消耗完畢
                         if (listHotel != null && listHotel.Count > 0)
                         {
                             hotelInfro = listHotel[0];
                             listHotel.Remove(hotelInfro);
                         }
                     }

                     // 開始模擬真正的資料同步操作
                     if (!string.IsNullOrEmpty(hotelInfro))
                     {
                         Thread.Sleep(1000);
                         Console.WriteLine($"我是執行緒ID{Thread.CurrentThread.ManagedThreadId.ToString()  },完成酒店【{hotelInfro}的資料同步處理");
                     }
                 }
             }));

             // 啟動執行緒
             taskList[i].Start();
         }
     }
 }

 

 

執行結果:

設定最多開5個執行緒

設定對多開10個執行緒

    ​這樣就達到了自動的動態控制執行緒建立,當然這個裡面還涉及到了執行緒同步等問題處理

 

總結:

 

    ​通過本篇文章,和大家分享了多執行緒的取與舍,多線的動態建立等等,在實際工作過程中,或多或多我們都會遇到這樣的場景,希望能夠有點幫助    ​

    ​好了,今天就說在這兒了,有什麼問題,大家可以多多交流,最後祝大家元旦快樂

猜您喜歡: 

 第一篇:聊聊多執行緒哪一些事兒(task)之 一建立執行與阻塞

 第二篇:聊聊多執行緒哪一些事兒(task)之 二 延續操作

 第三篇:聊聊多執行緒那一些事兒(task)之 三 非同步取消和非同步方法

 第四篇:聊聊多執行緒那一些事兒 之 四 經典應用(取與舍、動態建立)

END
為了更高的交流,歡迎大家關注我的公眾號,掃描下面二維碼即可關注,謝謝: