1.使用匿名管道实现 进程池
#pragma once
typedef void(*task_t)();
void task1()
{
cout << "游戏 刷新日志" << endl;
}
void task2()
{
cout << "游戏 刷新野区" << endl;
}
void task3()
{
cout << "游戏 检测软件是否更新,如果需要,就提示用户" << endl;
}
void task4()
{
cout << "游戏 用户释放技能,更新用户的HP和MP"<<endl;
}
void Loadtask(vector<task_t> *tasks)
{
tasks->push_back(task1);
tasks->push_back(task2);
tasks->push_back(task3);
tasks->push_back(task4);
}
//以上是头文件
//以下是程序;
//类似于内存池,每次使用都不再向系统申请进程,而是提前储备进程;
#include<cstdlib>
#include<cassert>
#include<time.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<iostream>
#include<vector>
#include<string>
#include<unistd.h>
using namespace std;
#include"main.h"
const int processnum = 5;//子进程数量=5
vector<task_t> tasks; //任务列表,全局变量
//先描述
class channel
{
public:
channel(int cmdfd ,pid_t slaverid,const string &processname)
:_cmdfd(cmdfd),
_slaverid(slaverid),
_processname(processname)
{}
public:
int _cmdfd; //发送任务的文件描述符
pid_t _slaverid; //子进程的pid
string _processname;//子进程的名字——方便我们打印日志;
};
void slaver()
{
//read(0);//直接从标准输入读
while (true)
{
int cmdcode = 0;
int n = read(0,&cmdcode,sizeof(int));//如果父进程不给子进程发任务?就会阻塞等待;
if (n == sizeof(int))
{
//执行cmdcode 对应的任务列表;
cout << getpid() << ":cmdcode:" << cmdcode << endl;//查看父进程给子进程派发的任务码;
if (cmdcode > 0 && cmdcode < tasks.size()) //满足条件运行函数;
tasks[cmdcode]();
}
if (n == 0)
{
break;
}
}
}
//函数传参,需要输入型,输出型,输入输出型参数;
//输入:const &: const引用
//输出:用指针
//输入输出:&引用
void InitProcessPool(vector<channel>* channels)
{
//方法二:确保,每个子进程都只有一个写端
vector<int> oldfds;
//1.初始化
for (int i = 0; i < processnum; i++)
{
int pipefd[2];//临时空间;
int n = pipe(pipefd);
assert(!n);//判断是否成功;
(void)n;
pid_t id = fork();
if (id == 0)//子进程读
{
for (auto fd : oldfds) close(fd); //关掉写端管道;
close(pipefd[1]);
dup2(pipefd[0], 0); //重定向,从标准输入中读
close(pipefd[0]);
slaver(); //执行相对应的任务;
cout << "process: " << getpid() << "quit" << endl;//打印子进程pid;退出进程;
exit(0);
}
//父进程写入
close(pipefd[0]);
//添加channel字段;
string name = "process-" + to_string(i);
channels->push_back(channel(pipefd[1], id, name));
oldfds.push_back((pipefd[1]));
}
}
void Debug(const vector<channel> &channels)
{
//test
for (const auto& c : channels)
{
cout << c._cmdfd << " " << c._slaverid << " " << c._processname << endl;
}
}
void Menu() //菜单
{
cout << "1.刷新日志" << endl;
cout << "2.刷新野怪" << endl;
cout << "3.检测更新" << endl;
cout << "4.更新状态" << endl;
cout << "0.退出游戏" << endl;
}
//控制子进程
void ctrlSlaver(const vector<channel>& channels)
{
int which = 0;
//int cnt = 5;
while(true)
{
int select = 0;
Menu(); //手动控制
cout << "please Enter@" << endl;
cin >> select;
if (select <= 0 || select >= 5) break;
//select >0 &&select<5
//1.选择任务
//int cmdcode = rand() % tasks.size();//创造一个随机数,当作任务;
int cmdcode = select-1;
//2.选择进程(负载均衡:不可以一直给某个进程派发任务,其他进程都闲着,要均衡发展这个叫做负载均衡)1.使用随机数 2.轮转l来实现负载均衡
//int processpos = rand() % channels.size(); //随机数
//cout << "father say:" << "cmdcode:" << cmdcode << "already send to" << channels[processpos]._slaverid << endl;
cout << "father say:" << "cmdcode:" << cmdcode << "already send to" << channels[which]._slaverid << endl;//轮转
//3.发送任务
//write(channels[processpos]._cmdfd, &cmdcode, sizeof(cmdcode)); //随机数版本
write(channels[which]._cmdfd, &cmdcode, sizeof(cmdcode)); //轮转
which++;
which %= channels.size();
//cnt--;
//sleep(1);
}
}
void QuitProcess(const vector<channel> &channels) //退出进程
{
//方法1:倒着回收
int last = channels.size() - 1;
for (int i = last; i >= 0; i--)
{
close(channels[i]._cmdfd);
waitpid(channels[i]._slaverid, nullptr, 0);
}
//方法二:在初始化时关闭子进程多余的管道;
//for (const auto& c : channels)
//{
// close(c._cmdfd);
//}
//for (const auto& c : channels) waitpid(c._slaverid, nullptr, 0); //等待退出
}
int main()
{
Loadtask(&tasks); //加载任务参数;
srand(time(nullptr) ^ getpid() ^ 1023); //种一个随机数种子
//再组织
vector<channel> channels;
//初始化-------bug?————画图就可以看出来,推荐画一下图!!!答:文件描述符随着创建子进程递增;
InitProcessPool(&channels); //初始化
Debug(channels); //测试
//2.开始控制子进程
ctrlSlaver(channels); //控制子进程
//3.清理收尾
QuitProcess(channels);
return 0;
}