C++多線程快速入門(五)簡單線程池設計

目錄

  • 設計思路
  • 主線程運行邏輯
  • task以及taskpool設計
  • 詳細流程講解
  • 完整代碼
  • 打印結果
  • 往期回顧

設計思路

線程池實際上就是一組線程,當我們需要異步執行一些任務時,經常要通過OS頻繁創建和銷毀線程,不如直接創建一組在程序生命周期內不會退出的線程。
當有任務需要被執行時,線程可以自動地去拿任務并執行,在沒有任務時,線程處于阻塞或者睡眠狀態。
我們選擇隊列存放任務,main函數中的thread作為任務的生產者,從隊列尾部插入任務。線程池中的線程作為消費者,從隊列頭部獲取任務。
復雜考慮的話,當線程池的線程在處理任務的過程中也產生了關聯任務,那么這個線程也是消費者。

隊列也可以設計得更加具有實用性,例如可以根據任務的優先級設計多個隊列,然后線程根據優先級獲取線程(也就是先查詢高優先級的隊列,為空再去查詢低優先級的隊列)。

綜上,我們的設計中會有多個線程訪問任務隊列,所以我們要解決線程池創建向隊列投放任務從隊列中獲取任務的線程互斥性。
同時線程池的清理退出線程池中的工作線程清理任務隊列,也是需要考慮的。
并且,為了能夠詳細獲知多線程獲取多任務的流程,我們需要對taskID和threadID進行輸出打印,std::cout并不是線程安全的,所以我們也要實現互斥地cout。
這里我們統一使用互斥量std::mutex+lock來實現互斥性

主線程運行邏輯

在這里插入圖片描述

task以及taskpool設計

Task
在這里插入圖片描述
TaskPool
在這里插入圖片描述
內部變量
在這里插入圖片描述

詳細流程講解

1、創建線程池對象時,調用構造函數TaskPool(),并初始化布爾類型的標志m_bRunning為false,表示此時線程池對象中的線程不工作
2、調用線程池對象的初始化函數init(),這里默認的線程數為5。

  • m_bRunning置為true,表示線程池對象中的線程應該開始運行了
  • 然后通過for循環,每次構造一個新的線程對象,并且綁定一個線程函數TaskPool::threadFunc,創建之后線程就開始工作了。然后打印出當前的線程id,注意此時需要上鎖,保證cout輸出正常。然后將線程對象送入m_threads數組
    3、接下來看看構造出來的每個線程在干啥:
while(true)
{{上鎖,訪問任務隊列// 注意由于隊列本身是個多線程共享資源,所以對于隊列取元素以及狀態判斷都是要先加鎖再操作隊列為空則一直循環  // 為什么要用循環判斷呢?// 這是因為wait()從阻塞到返回,不一定就是由于notify_one()函數造成的,還有可能由于系統的不確定原因喚醒(可能和條件變量的實現機制有關),這個的時機和頻率都是不確定的,被稱作偽喚醒,如果在錯誤的時候被喚醒了,執行后面的語句就會錯誤,所以需要再次判斷隊列是否為空,如果還是為空,就繼續wait()阻塞。{如果m_bRunning為false,說明此時應該中止線程操作,所以需要連著跳出兩個循環,所以直接用goto label吧否則就一直等待在這兒,wait()可以讓線程陷入休眠狀態,在消費者生產者模型中,如果生產者發現隊列中沒有東西,就可以讓自己休眠.}此時 獲取隊頭元素,并將隊頭元素出隊}// 為了減少鎖的粒度,接下來的操作不需要加鎖了,因為已經拿到了隊列中的元素執行Task對象的doIt()方法,也就是打印任務id和線程id
}
label :
打印當前線程id

4、for循環,創建10個Task對象,然后調用addTask方法,將task送入線程池中的任務隊列。顯然push操作是互斥的,所以需要先上鎖。然后打印任務id和線程id,最后通過條件變量的notify_one方法,通知一個掛起的線程去消費隊列里面的任務。

5、等待一段時間
6、調用線程池對象的stop方法,先設置m_bRunning標志為false,然后通過條件變量的notify_all方法,通知掛起的或者正在運行的所有線程,結束線程函數運行。然后等待所有線程join之后,退出。
7、跳出主線程,開始調用TaskPool對象的析構函數,也就是執行removeAllTasks方法,也就是將任務隊列里面的存的task指針進行reset,也就是減引用計數,shared_ptr指針如果引用計數減為0,會自動調用析構函數。為了線程安全,我們同樣需要對這塊代碼進行加鎖。

完整代碼

c++實現簡單線程池代碼

打印結果

Init a thread, id: 2
Init a thread, id: 3
Init a thread, id: 4
Init a thread, id: 5
Init a thread, id: 6
add a Task, id: 0, thread id is: 1
add a Task, id: 1, thread id is: 1
add a Task, id: 2, thread id is: 1
handle a task ,TaskID is: 1, thradID is:3
a task destructed , TaskID is: 1, thradID is:3
handle a task ,TaskID is: 2, thradID is:4
a task destructed , TaskID is: 2, thradID is:4
handle a task ,TaskID is: 0, thradID is:2
a task destructed , TaskID is: 0, thradID is:2
add a Task, id: 3, thread id is: 1
handle a task ,TaskID is: 3, thradID is:3
a task destructed , TaskID is: 3, thradID is:3
handle a task ,TaskID is: 4, thradID is:5
add a Task, id: 4, thread id is: 1
a task destructed , TaskID is: 4, thradID is:1
add a Task, id: 5, thread id is: 1
handle a task ,TaskID is: 5, thradID is:4
a task destructed , TaskID is: 5, thradID is:4
add a Task, id: 6, thread id is: 1
handle a task ,TaskID is: 6, thradID is:4
a task destructed , TaskID is: 6, thradID is:4
handle a task ,TaskID is: 7, thradID is:2
add a Task, id: 7, thread id is: 1
a task destructed , TaskID is: 7, thradID is:1
add a Task, id: 8, thread id is: 1
add a Task, id: 9, thread id is: 1
handle a task ,TaskID is: 9, thradID is:6
a task destructed , TaskID is: 9, thradID is:6
handle a task ,TaskID is: 8, thradID is:5
a task destructed , TaskID is: 8, thradID is:5
exit thread , threadID:3
exit thread , threadID:4
exit thread , threadID:2
exit thread , threadID:5
exit thread , threadID:6Process finished with exit code 0

往期回顧

C++多線程快速入門(四)shared_mutex以及讀寫鎖應用
C++多線程快速入門(三):生產者消費者模型與條件變量使用
C++多線程快速入門(二)共享數據同步以及數據競爭
C++多線程快速入門(一):基本&常用操作

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/376821.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/376821.shtml
英文地址,請注明出處:http://en.pswp.cn/news/376821.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C--函數

函數:具有特定功能的代碼段,分為庫函數,自定義函數. 函數定義: 函數返回值類型 函數名(形式參數列表) { 代碼段; return 返回值; } 注意:每個函數返回值最多只有一個.return是一個函數結束的標志. 形式參數(形參):函數定義時使用的虛擬參數名,用以接收函數調用是傳遞過來的實際…

公式系統 - TradeBlazer公式基礎 - Bar數據

Bar數據 在介紹Bar數據之前,首先,我們需要討論一下TradeBlazer公式的計算方法,針對上面介紹的各種公式類型,包含公式應用,在公式進行計算時,都是建立在基本數據源(Bar數據)之上,我們這里所謂的B…

C++網絡編程快速入門(一):TCP網絡通信基本流程以及基礎函數使用

目錄流程概述服務器端代碼實現客戶端代碼實現函數和結構講解sockaddr_in和sockaddrsocket : 創建一個socket連接bind :綁定地址以及端口號問題流程概述 客戶端與服務器之間的網絡通信基本原理如下所示,復雜一點的架構可能會添加消息中間件。…

php 字符串處理

addcslashes — 為字符串里面的部分字符添加反斜線轉義字符addslashes — 用指定的方式對字符串里面的字符進行轉義bin2hex — 將二進制數據轉換成十六進制表示chop — rtrim() 的別名函數chr — 返回一個字符的ASCII碼chunk_split — 按一定的字符長度將字符串分割成小塊conve…

使用前端框架Foundation 4來幫助簡化響應式設計開發

日期:2013-3-12 來源:GBin1.com Foundation是一套使用廣泛的前端開發套件,可以幫助你快速的網站。最近ZURB發布了一個新版本的Foundation 4前端框架,能夠有效的幫助你快速的開發響應式的網站。 和另外一個套知名的前端框架BootSt…

C++網絡編程快速入門(二):Linux下使用select演示簡單服務端程序

目錄select參數解釋select使用規范select使用缺點基本流程實例代碼通信效果演示往期文章select參數解釋 extern int select (int __nfds, fd_set *__restrict __readfds,fd_set *__restrict __writefds,fd_set *__restrict __exceptfds,struct timeval *__restrict __timeout)…

Android轉載一:Android文件命名規范

REF:http://blog.csdn.net/gulianchao/article/details/23391651 (一) Layout命名 1.contentview命名:activity_功能模塊.xml 例如:activity_main.xml、activity_more.xml 2.Dialog命名:dialog_描述.xml …

[轉]XBRL應用軟件分類

1) 分類標準編輯軟件(Taxonomy editor): 分類標準是XBRL技術的應用基礎,每一個采用XBRL技術的國家都必須先按各國的GAAP制訂XBRL分類標準,上市公司才能據以編制實例文件。由于一套XBRL 2.0或2.1版分類標準必須包含至少一份XML Schema文…

C++網絡編程快速入門(三):阻塞與非阻塞式調用網絡通信函數

目錄阻塞與非阻塞定義send與recvconnect一些問題為什么要將監聽socket設置為非阻塞阻塞與非阻塞定義 阻塞模式指的是當前某個函數執行效果未達預期,該函數會阻塞當前的執行線程,程序執行流在超時時間到達或者執行成功后恢復原有流程。非阻塞模式相反&am…

css3實現頭像旋轉360度

css樣式: .div a img{ width: 88px; height: 88px; border-radius: 88px; transition: all 1.2s ease-out 0s;}.div a img:hover{ -webkit-transform:rotate(360deg); -moz-transform:rotate(360deg); -o-transform:rotate(360deg); -ms-transform:rotate(360deg); transform:r…

POJ 2488 深搜

POJ 2488 深搜 要求字典序的順序。 1 #include <iostream>2 #include <stdio.h>3 #include <string.h>4 using namespace std;5 int n,m,cnt;6 bool success;7 bool sign[30][30];8 int step[30][2];9 int dir[8][2]{ 10 -2,-1,-2,1, 11 …

socket 端口和地址復用

https://blog.csdn.net/weibo1230123/article/details/79978745 https://blog.csdn.net/weixin_42157432/article/details/115560824 在linux socket網絡編程中&#xff0c;大規模并發TCP或UDP連接時&#xff0c;經常會用到端口復用&#xff1a; int opt 1; if (setsockopt…

MyEclipse老是彈出problem occurred窗口

有的時候是因為jsp頁面中的java腳本有誤&#xff0c;比如說<% String name"";>就會出現錯誤&#xff0c;因為結束標簽少了一個百分號&#xff05;。轉載于:https://www.cnblogs.com/passer1991/archive/2013/03/15/2961624.html

C++網絡編程快速入門(四):EPOLL模型使用

目錄基本使用方法step1:創建epollfdstep2:將fd綁定到epollfdstep3:調用epoll_wait檢測事件epoll_wait與poll、select區別所在水平觸發與邊緣觸發基本使用方法 step1:創建epollfd 創建一個epollfd&#xff0c;若epoll_create調用成功&#xff0c;則返回一個非負值的epollfd&am…

Mysql中代替like模糊查詢的一種方法

使用Mysql的函數instr,可代替傳統的like方式查詢,并且速度更快。 instr函數&#xff0c;第一個參數是字段&#xff0c;第二個參數是要查詢的串&#xff0c;返回串的位置&#xff0c;第一個是1&#xff0c;如果沒找到就是0. 例如&#xff1a; select username from prefix_user …

兩種大小端判斷的方式

網絡通信是按照字節流進行數據交換的&#xff0c;主機根據不同的CPU型號可能是大段存儲&#xff0c;也可能是小端存儲。而網絡字節序在TCP/IP協議中已經規定好了&#xff0c;采用大端的排序方式。 所以網絡通信中一般將需要傳輸的整數型值轉換成網絡字節序。 從本機字節序轉換成…

把數據庫復制成腳本(包含遠程以及數據庫數據)

1.啟動VS 2.服務器資源管理器 3.連接需要的數據庫 4.右鍵數據庫 選擇publist to provider.... 5.剩下的 選擇數據庫 選擇存放地址 下一步 這方法應該是用在把08的數據還原到05上面 明天用這個方法去盜取哈公司的數據庫 看行不行轉載于:https://www.cnblogs.com/Rock-Lee/a…

代理模式用來初始化的延遲下載

package 設計模式; //代理模式實現延遲加載來減小啟動時間 //數據庫查詢接口 interface IDBQery{ public String request(); }class DBQuery implements IDBQery {//創建一個DBQery非常耗時的&#xff0c;這里面我可以在需要DBQuery的時候在創建public DBQuery(){try {Thread.s…

Linux網絡故障排查命令(ifconfig、ping、telnet、netstat、lsof、nc、curl、tcpdump)

目錄ifconfig-s&#xff0c;顯示網卡信息的精簡列表-a、up、down將IP地址綁定到某個網卡&#xff0c;以及解綁操作pingtelnetnetstatlsofnc模擬一個服務器程序和客戶端程序進行通信發送文件curltcpdump參數連接一個正常的監聽端口ifconfig 該命令用來查看當前系統的網卡和IP地…

My Oracle Support Metalink站點最近將放棄flash界面轉而使用ADF HTML

根據oracle官方博客的報道《The New My Oracle Support User Interface (HTML-based) 》&#xff0c; MY ORACLE SUPPORT開發team會在最近將support.oracle.com站點從原來的flash界面遷移到基于ADF HTML的用戶界面上。 實際上在2012年的 January 27&#xff0c; MOS開發team就…