21xrx.com
2024-11-05 17:30:49 Tuesday
登录
文章检索 我的文章 写文章
使用C++实现进程池:博客园分享
2023-07-07 13:48:49 深夜i     --     --
C++ 进程池 实现 博客园 分享

在Unix系统下,进程池是一种常见的并发处理方式。传统的进程处理方式会导致系统资源的消耗过大,进程池的方式可以通过预分配一定数量的进程,进程之间共享资源,减少了进程的创建和销毁带来的资源消耗。本文将介绍如何使用C++实现一个简单的进程池。

首先,定义进程池类ProcessPool,该类包括一个内部的结构体Process,用于存储子进程相关信息,以及一个构造函数和一个Run函数用于启动进程池。


class ProcessPool

{

private:

  struct Process

  {

    pid_t pid;       //子进程pid

    int pipefd[2];     //父子进程通信管道

  };

public:

  ProcessPool(int listenfd, int process_num = 8);

  ~ProcessPool();

  void Run();

private:

  static const int MAX_PROCESS_NUM = 16;

  static const int MAX_EVENT_NUM = 10000;

  int m_process_num;     //进程池内进程数量

  int m_listenfd;       //监听套接字

  int m_idx;         //每个进程在进程池中的序号

  int m_epollfd;       //用于事件通知的epoll

  epoll_event m_events[MAX_EVENT_NUM];  //epoll事件

  Process m_sub_process[MAX_PROCESS_NUM]; //进程池

  static ProcessPool* m_instance;     //单例模式对象

};

然后,在ProcessPool类的构造函数中初始化进程池,并设置每个子进程的相应事件处理函数。


ProcessPool::ProcessPool(int listenfd, int process_num) : m_listenfd(listenfd), m_process_num(process_num), m_idx(-1)

{

  assert(process_num > 0 && process_num <= MAX_PROCESS_NUM);

  m_instance = this;

  for (int i = 0; i < m_process_num; ++i)

  {

    int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, m_sub_process[i].pipefd);

    assert(ret != -1);

    m_sub_process[i].pid = fork();

    assert(m_sub_process[i].pid >= 0);

    if (m_sub_process[i].pid > 0)

    {

      close(m_sub_process[i].pipefd[1]);

      continue;

    }

    else

    {

      close(m_sub_process[i].pipefd[0]);

      m_idx = i;

      break;

    }

  }

}

void ProcessPool::Run()

{

  if (m_idx == -1)

  {

    SetupSIGCHLDHandler();  //父进程安装信号处理函数

    SetupEpoll();       //父进程创建epoll

    //将监听套接字加入epoll监控

    AddFd(m_epollfd, m_listenfd);

    //循环处理事件

    while (true)

    {

      int event_num = epoll_wait(m_epollfd, m_events, MAX_EVENT_NUM, -1);

      if (event_num < 0 && errno != EINTR)

      {

        printf("epoll failed\n");

        break;

      }

      for (int i = 0; i < event_num; ++i)

      {

        int fd = m_events[i].data.fd;

        if (fd == m_listenfd)

        {

          int clientfd = Accept(m_listenfd);

          AddFd(m_epollfd, clientfd);

        }

        else if (m_events[i].events & EPOLLIN)

        {

          //接收到客户端数据

          Request* request = new Request();

          request->m_connfd = fd;

          AddTask(request);

        }

        else if (m_events[i].events & EPOLLOUT)

        {

          //响应客户端数据

          Response* response = new Response();

          response->m_connfd = fd;

          AddTask(response);

        }

        else

        {

          printf("something else happened\n");

        }

      }

    }

  }

  else

  {

    SetupSIGCHLDHandler();   //子进程安装信号处理函数

    SetupEpoll();        //子进程创建epoll

    //子进程监听管道

    AddFd(m_epollfd, m_sub_process[m_idx].pipefd[0]);

    //循环处理事件

    while (true)

    {

      int event_num = epoll_wait(m_epollfd, m_events, MAX_EVENT_NUM, -1);

      if (event_num < 0 && errno != EINTR)

      {

        printf("epoll failed\n");

        break;

      }

      for (int i = 0; i < event_num; ++i)

      {

        int fd = m_events[i].data.fd;

        if (fd == m_sub_process[m_idx].pipefd[0])

        {

          //接收到父进程的任务

          Request* request = NULL;

          while ((request = RecvTask()) != NULL)

          {

            ProcessRequest(request);

          }

        }

      }

    }

  }

}

最后,可以看到ProcessPool的Run方法中包括了父进程和子进程的运行逻辑。子进程通过监听管道,接收父进程的任务,处理完后通过管道向父进程返回结果。而父进程则监听套接字,接收客户端连接,并将请求任务添加到进程池中去,等待子进程处理。

进程池的实现为高效的并发处理提供了一种可行的方案。本文介绍的进程池实现思路简单清晰,通过使用C++提供的类与对象的封装,使得代码的可读性和可维护性得到了很大的提高,也为使用C++进行进程池开发提供了参考。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复