【Linux】進程通信實戰 —— 進程池項目

在這里插入圖片描述
送給大家一句話:
沒有一顆星,會因為追求夢想而受傷,當你真心渴望某樣東西時,整個宇宙都會來幫忙。 – 保羅?戈埃羅 《牧羊少年奇幻之旅》

🏕?🏕?🏕?🏕?🏕?🏕?
🗻🗻🗻🗻🗻🗻


進程通信實戰 —— 進程池項目

  • 1 ??知識回顧
  • 2 ??項目介紹
  • 3 ??項目實現
    • 3.1 ?創建信道和子進程
    • 3.2 ?建立任務
    • 3.3 ?控制子進程
    • 3.4 ?回收信道和子進程
  • 4 ??總結
  • Thanks?(・ω・)ノ謝謝閱讀!!!
  • 下一篇文章見!!!

1 ??知識回顧

在之前的講解中,我們深入探討了以下幾個方面:

  1. 父子進程的創建與管理:我們詳細講解了父子進程是如何建立的,以及子進程如何繼承父進程的代碼和數據。子進程通常用于完成特定的任務。
  2. 文件操作:我們學習了如何使用 read 和 write 操作文件,并了解了文件描述符(fd)的概念,從而能夠在文件中進行信息的讀取和寫入。
  3. 進程間通信:我們介紹了匿名管道,這是一種父子進程間進行通信的方式。通過共享資源,父子進程可以實現數據的傳遞和同步。

在接下來的內容中,讓我們把所學知識來進行運用,我們將探討進程池的概念和實現細節。

2 ??項目介紹

進程池是一種用于管理和復用進程的技術,它可以有效地管理系統資源并提高程序的性能和效率。通過維護一組預先創建的進程與管道,進程池可以避免頻繁地創建和銷毀進程,從而減少了系統開銷和資源浪費。
在這里插入圖片描述
主要使用的是池化技術的思想:

池化技術是一種廣泛應用于系統開發中的優化策略,旨在通過復用資源來提高性能和效率。池化技術的核心思想是預先分配一組資源,并在需要時進行復用,而不是每次都重新創建和銷毀資源。

池化技術(Pooling)涉及創建和管理一組預先分配的資源,這些資源可以是進程、線程、數據庫連接或對象實例。在池化系統中,當請求到達時,它會從池中獲取一個空閑資源,使用完畢后將其歸還池中。這種方法避免了頻繁的創建和銷毀操作,從而顯著減少了系統開銷。

進程池就是通過預先創建若干個進程與管道,在需要進行任務時,選擇一個進程,通過管道發送信息,讓其完成工作。

進程池在實際項目中有廣泛的應用,尤其是在處理大量并發任務時,例如:網絡服務器中的請求處理、數據處理以及計算密集型任務。通過合理配置進程池的大小和參數,可以有效控制系統負載,提高整體響應速度。

3 ??項目實現

3.1 ?創建信道和子進程

首先我們需要建立一個信道類,來儲存管道及其對應的子進程信息。

//信道類
class Channel
{
public:Channel(pid_t id , int wfd , std::string name):_id(id) , _wfd(wfd) , _name(name)    {}~Channel(){}void Close(){close(_wfd);}//關閉管道時需要等待對應子進程結束void WaitSub(){pid_t rid = waitpid(_id, nullptr, 0);if (rid > 0){std::cout << "wait " << rid << " success" << std::endl;}}pid_t GetId(){ return _id;}int GetWfd(){ return _wfd;}std::string GetName(){return _name ;}
private:pid_t _id ;//對應 子進程 idint _wfd  ;//寫入端std::string _name ; //管道名稱
};

然后我們就建立若干個信道與子進程,創建子進程與信道的時候,把信息插入到信道容器中,完成儲存。子進程需要阻塞在讀取文件,等待父進程寫入信息:

void CreateChannel(int num , std::vector<Channel>* channel)
{//初始化任務InitTask();for(int i = 0 ; i < num ; i++){//創建管道int pipefd[2] = {0};int n = pipe(pipefd);if(n != 0){std::cout << "create pipe failed!" << std::endl;}//創建子進程pid_t id = fork();if(id == 0){//子進程 --- 只讀不寫close(pipefd[1]);work(pipefd[0]);close(pipefd[0]);exit(0);}//父進程close(pipefd[0]);std::string name = "Channel - " + std::to_string(i);//儲存信道信息channel->push_back( Channel(id , pipefd[1] , name) );}
}

這里提一下傳參的規范:

  • const &:表示輸出型參數,即該參數是輸入型,不會被修改。常用于傳遞不需要修改的對象或數據。
  • &:表示輸入輸出型參數,即該參數既是輸入參數,又是輸出參數,函數可能修改其內容。
  • * :表示輸出型參數,通常用于傳遞指針,函數通過指針參數返回結果給調用者。

進行一下測試,看看是否可以這正常建立信道與子進程;

int main(int argc , char* argv[])
{//1. 通過main函數的參數 int argc char* argv[] (./ProcessPool 5) //判斷要創建多少個進程if(argc != 2){std::cout << "請輸入需要創建的信道數量 :"  << std::endl;}std::vector<Channel> channel;int num = std::stoi(argv[1]);//2. 創建信道和子進程CreateChannel(num , &channel);//測試:for(auto t :  channel){std::cout<< "==============="<<std::endl;std::cout<< "信道對應 name :" << t.GetName() <<std::endl;std::cout<< "信道對應子進程 pid :" << t.GetId() <<std::endl;std::cout<< "信道對應寫端 wfd :" << t.GetWfd() <<std::endl;}return 0;
}

在這里插入圖片描述
完美,可以正常創建!!!

3.2 ?建立任務

完成了信道與子進程的創建,接下來我們就來設置一些任務。我們在.hpp文件里直接把聲明定義寫在一起,確保代碼的模塊化和可維護性。

void Print()
{std::cout << "this is Print()"<< std::endl;
}void Fflush()
{std::cout << "this is Fflush()"<< std::endl;
}void Scanf()
{std::cout << "this is Scanf()"<< std::endl;
}

然后通過函數指針數來儲存這些函數,因為子進程會繼承父進程的數據,這樣通過一個數字下標即可確定調用的函數。只需要傳入 4 個字節的int類型,最大程度的減少了通信的成本!!!

#pragma once#include <iostream>
#include <ctime>
#include <cstdlib>
#include <sys/types.h>
#include <unistd.h>#define TaskNum 3
//這個文件里是任務函數
typedef void(*task_t)();task_t tasks[TaskNum];
//...
//...三個函數
//...
void InitTask()
{srand(time(nullptr) ^ getpid() ^ 17777);tasks[0] = Print;tasks[1] = Fflush;tasks[2] = Scanf;
}
//執行任務!!!
void ExecuteTask(int num)
{if(num < 0 || num > 2) return;tasks[num]();
}
//隨機挑選一個任務
int SelectTask()
{return rand() % TaskNum;
}

3.3 ?控制子進程

首先通過 SelectTask() 選擇一個任務,然后選擇一個信道和子進程。需要注意的是,這里要依次調用每一組子進程,采用輪詢(Round-Robin)方案,以盡可能實現負載均衡。 然后發送任務(向信道寫入4字節的數組下標)

int SelectChannel(int n)
{//靜態變量做到輪詢方案static int next = 0;int channel = next;next++;next %= n;return channel;
}
void SendTaskCommond(Channel& channel , int TaskCommand  )
{//寫入對應信息write(channel.GetWfd() , &TaskCommand , sizeof(TaskCommand));
}void CtrlProcessOnce(std::vector<Channel>& channel)
{//選擇一個任務int TaskCommand = SelectTask();//選擇一個進程與信道int ChannelNum = SelectChannel(channel.size());//發送信號//測試std::cout << "taskcommand: " << TaskCommand << " channel: "<< channel[ChannelNum].GetName() << " sub process: " << channel[ChannelNum].GetId() << std::endl; SendTaskCommond(channel[ChannelNum] ,TaskCommand);}

我們寫入之后,子進程就可以讀取任務并執行,注意子進程讀取只讀4個字節!!!如果讀取的個數不正確,那么就出現了錯誤,需要報錯!!!

//子進程運行函數
void work(int rfd)
{while(true){int Commond = 0;//等待相應int n = read(rfd , &Commond , sizeof(Commond));if(n == sizeof(int)){std::cout << "pid is : " << getpid() << " handler task" << std::endl;//執行命令std::cout << "commond :" << Commond << std::endl;ExecuteTask(Commond);}//寫端關閉else if(n == 0){std::cout << "sub Process:" << getpid() << std::endl;break;}}
}
//...//創建子進程pid_t id = fork();if(id == 0){//子進程 --- 只讀不寫close(pipefd[1]);work(pipefd[0]);close(pipefd[0]);exit(0);}
//...

進行一下測試:
在這里插入圖片描述
成功執行任務!!!

3.4 ?回收信道和子進程

首先關閉信道寫端,這樣子進程會自己退出,然后父進程等待子進程退出(wait等待子進程 )不要出現僵尸進程 !!!

注意由于子進程會繼承父進程的數據,所以一個信道實際上會有多個寫端。為了不必要的錯誤,分開集中操作:先關閉所有寫端,再等待所以子進程。

void CleanUpChannel(std::vector<Channel>& channel)
{for(auto t : channel){t.Close();}for(auto t : channel){t.WaitSub();}
}

測試一下:
在這里插入圖片描述
5 個子進程成功退出釋放!!!

4 ??總結

這樣,我們的進程池項目就完成了。不過,實際上我們還可以進一步優化,比如優化 work 函數,將其設置為回調函數,以實現完全解耦。

盡管如此,目前的實現已經能夠滿足我們的項目需求。一個面向過程的進程池項目就此完成!!!

Thanks?(・ω・)ノ謝謝閱讀!!!

下一篇文章見!!!

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

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

相關文章

flink cdc mysql整理與總結

文章目錄 一、業務中常見的需要數據同步的場景CDC是什么FlinkCDC是什么CDC原理為什么是FlinkCDC業務場景flink cdc對應flink的版本 二、模擬案例1.阿里云flink sql2.開源flink sql(單機模式)flink 安裝安裝mysql3.flink datastream 三、總結 提示&#xff1a;以下是本篇文章正文…

mac中文件夾怎么顯示.git隱藏文件

1. 打開終端應用程序&#xff0c;然后進入到包含.git文件夾的目錄&#xff0c;可以使用以下命令來顯示隱藏文件和文件夾&#xff1a; defaults write com.apple.finder AppleShowAllFiles YES 2. 然后重啟 Finder&#xff1a; killall Finder

kali基本掃描工具(自帶)

免責聲明:本文僅做技術交流與學習...請勿非法破壞... 詳細用法: 命令 -h/百度/翻譯 fping 用法 hostlist 文件里面為ip fping -a -q -f hostlist -a 只看存活的 fping -g 202.100.1.1 202.100.1.255 -a -q > Ahost 輸出到Ahost文件上 nping nping -c 1 201.100.2.155-244 …

工具方法 - 如何在網上找資料

在查詢USB相關的技術資料時&#xff0c;官網的文檔中心里找到個spec的記錄&#xff0c;但下載鏈接沒有。然后在Google上搜索&#xff1a; fileytpe:pdf my_keyword 只找到一個收費的文檔下載網站&#xff0c;這讓我不開心。 于是在Yandex上搜了下&#xff0c;找到了兩個網站可以…

香橙派AIpro使用SSH遠程登錄

香橙派AIpro可以連接HDMI顯示器使用&#xff0c;也可以遠程登錄。這里采用MobaXterm軟件遠程登錄開發板。 首先要使得控制電腦和香橙派開發板連接到同一個局域網&#xff0c;兩者的IP地址能夠ping通。在Windows 下可以使用MobaXterm 遠程登錄開發板&#xff0c;首先新建一個ss…

屬于程序員的浪漫,一顆會跳動的心!!!

繪制一顆會跳動的心? 嘿嘿 可以說是程序員的專屬浪漫了吧&#xff0c;就像點燃一顆LED燈一樣&#xff1f;&#xff08;我瞎說的啊&#xff0c;大家別當真&#xff0c;我很菜的&#xff01;&#xff01;&#xff01;&#xff01;&#xff09; 程序就在下面啦&#xff0c;然…

hive結合Hbase實現實時數據處理和批量分析

問題背景 Hive主要設計為一個用于大數據集的批處理查詢引擎&#xff0c;并不是為實時查詢或實時數據更新而設計的。它主要用于執行數據摘要、查詢和分析。因此&#xff0c;Hive本身不支持實時數據更新或實時查詢&#xff0c;它更適合用于對大量數據進行批量處理和分析。 分析…

Java8Stream

目錄 什么是Stream? IO流&#xff1a; Java8Stream&#xff1a; 什么是流&#xff1f; stream圖解 獲取流 集合類&#xff0c;使用 Collection 接口下的 stream() 代碼 數組類&#xff0c;使用 Arrays 中的 stream() 方法 代碼 stream&#xff0c;使用 Stream 中的…

重生之 SpringBoot3 入門保姆級學習(02、打包部署)

重生之 SpringBoot3 入門保姆級學習&#xff08;02、打包部署&#xff09; 1.6 打包插件1.7 測試 jar 包1.8 application.properties 的相關配置 1.6 打包插件 官網鏈接 https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-starte…

【Python】 XGBoost模型的使用案例及原理解析

原諒把你帶走的雨天 在漸漸模糊的窗前 每個人最后都要說再見 原諒被你帶走的永遠 微笑著容易過一天 也許是我已經 老了一點 那些日子你會不會舍不得 思念就像關不緊的門 空氣里有幸福的灰塵 否則為何閉上眼睛的時候 又全都想起了 誰都別說 讓我一個人躲一躲 你的承諾 我竟然沒懷…

自學動態規劃—— 一和零

一和零 474. 一和零 - 力扣&#xff08;LeetCode&#xff09; 其實遇到這種還好說&#xff0c;我寧愿遇見這種&#xff0c;也不想遇見那些奇奇怪怪遞推公式的題目。 這里其實相當背包要滿足兩個條件&#xff0c;所以我們可以將dp開成二維的&#xff0c;之后的操作&#xff0…

Kubernetes(K8S) 集群環境搭建指南

Kubernetes&#xff08;簡稱K8s&#xff09;是一個開源的容器編排平臺&#xff0c;旨在自動化部署、擴展和管理容器化應用。K8S環境搭建過程比較復雜&#xff0c;涉及到非常多組件安裝和系統配置&#xff0c;本文將會詳細介紹如何在服務器上搭建好Kubernetes集群環境。 在學習…

C語言---求一個整數存儲在內存中的二進制中1的個數--3種方法

//編寫代碼實現&#xff1a;求一個整數存儲在內存中的二進制中1的個數 //第一種寫法 /*int count_bit_one(unsigned int n) {int count 0;while (n )//除到最后余數是0&#xff0c;那么這個循環就結束了{//這個題就是可以想成求15的二進制的過程//每次都除以2&#xff0c;余數…

跟小伙伴們說一下

因為很忙&#xff0c;有一段時間沒有更新了&#xff0c;這次先把菜鳥教程停更一下&#xff0c;因為自己要查缺補漏一些細節問題&#xff0c;而且為了方便大家0基礎也想學C語言&#xff0c;這里打算給大家開一個免費專欄&#xff0c;這里大家就可以好好學習啦&#xff0c;哪怕0基…

面試題·棧和隊列的相互實現·詳解

A. 用隊列實現棧 用隊列實現棧 實現代碼如下 看著是隊列&#xff0c;其實實際實現更接近數組模擬 typedef struct {int* queue1; // 第一個隊列int* queue2; // 第二個隊列int size; // 棧的大小int front1, rear1, front2, rear2; // 兩個隊列的首尾指針 } MyS…

圖像處理ASIC設計方法 筆記25 紅外成像技術:未來視覺的革命

在當今科技飛速發展的時代,紅外成像技術以其獨特的優勢,在醫療、工業檢測等多個領域扮演著越來越重要的角色。本章節(P146 第7章紅外焦平面非均勻性校正SoC)將深入探討紅外成像系統中的關鍵技術——非均勻性校正SoC,以及它如何推動紅外成像技術邁向新的高度。 紅外成像系統…

6.Redis之String命令

1.String類型基本介紹 redis 所有的 key 都是字符串, value 的類型是存在差異的~~ 一般來說,redis 遇到亂碼問題的概率更小~~ Redis 中的字符串,直接就是按照二進制數據的方式存儲的. (不會做任何的編碼轉換【講 mysql 的時候,知道 mysql 默認的字符集, 是拉丁文,插入中文…

Jenkins--從入門到入土

Jenkins–從入門到入土 文章目錄 Jenkins--從入門到入土〇、概念提要--什么是CI/DI&#xff1f;1、CI&#xff08;Continuous Integration&#xff0c;持續集成&#xff09;2、DI&#xff08;DevOps Integration&#xff0c;DevOps 集成&#xff09;3、解決的問題 一、Jenkins安…

iOS 開發系列:基于VNRecognizeTextRequest識別圖片文字

1.添加Vision Kit依賴 在項目設置中點擊"General"選項卡&#xff0c;然后在"Frameworks, Libraries, and Embedded Content"&#xff08;框架、庫和嵌入內容&#xff09;部分&#xff0c;點擊""按鈕。搜索并選擇"Vision.framework"。…

[AIGC] flink sql 消費kafka消息,然后寫到mysql中的demo

這是一個使用 Flink SQL 從 Kafka 中消費數據并寫入 MySQL 的示例。在這個示例中&#xff0c;我們將假設有一個 Kafka 主題 “input_topic”&#xff0c;它產生格式為 (user_id: int, item_id: int, behavior: string, timestamp: long) 的數據&#xff0c;我們需要把這些數據寫…