首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Linux】命名管道的妙用:实现进程控制与实时字符交互

【Linux】命名管道的妙用:实现进程控制与实时字符交互

作者头像
Yui_
发布于 2025-06-02 04:37:18
发布于 2025-06-02 04:37:18
7700
代码可运行
举报
文章被收录于专栏:Yui编程知识Yui编程知识
运行总次数:0
代码可运行

1. 进程池

在匿名管道学习后,我们已经实现了一个基于匿名管道的简单进程控制,现在我们学习了命名管道来试试用命名管道来实现进程控制吧。 正在匿名管道时,我们已经实现了进程的控制,命名管道只要在其基础上进行一些修改就可以了。

2. 进程池的功能

2.1 可被执行的任务

进程池是为了实现对进程的控制,通过父进程对众多子进程的管理,实现高效的进程执行任务。 我们可以先虚构一些任务出来,后续的子进程就来执行这些任务:

  1. 打印日志
  2. 将数据插入数据库
  3. 请求网络 可以用这些任务可以写成单独的函数,然后用一个类来管理这些任务。 类的成员变量: 利用哈希表来存储这些任务 类的功能有:
  4. 展示可执行的任务
  5. 执行任务 具体的代码如下:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef void(*task_t)(void);/*函数指针*/

//任务集
void printLog()
{
    std::cout<<"PID:"<<getpid()<<",正在打印日志..."<<std::endl;
}

void MySQLInsert()
{
    std::cout<<"PID"<<getpid()<<",正在将数据插入数据库..."<<std::endl;
}

void NetRequest()
{
    std::cout<<"PID"<<getpid()<<",正在请求网络..."<<std::endl;
}



class Task{
private:
    std::unordered_map<std::string,task_t> st_;
public:
    Task()
    {
        st_ = {{"打印日志",printLog},{"插入数据",MySQLInsert},{"请求网络",NetRequest}};
    }
    void showTask()/*展示任务*/
    {
        std::cout<<"可选任务:"<<std::endl;
        std::cout<<" {";
        for(auto&s:st_)
        {
            std::cout<<s.first<<' ';
        }
        std::cout<<'}'<<std::endl;
    }
    void curTask(const std::string& t)/*执行任务*/
    {
        if(st_.find(t) == st_.end())
        {
            std::cout<<"没有这个任务哦~"<<std::endl;
        }
        else
        {
            st_[t]();/*调用任务*/
        }
    }
};

2.2 进程控制(重点)

进程控制是进程池的重点、核心。我们实现进程池也就是为了执行对进程的控制。 如何控制进程呢?

  1. 创建进程
  2. 展示空闲子进程
  3. 子进程等待任务的发配
  4. 父进程为子进程发配任务
  5. 杀死/回收进程
2.2.1 子进程类

为了实现这些功能,同样我们用一个类来封装它们。除此之外,还记得我们在实现基于匿名管道的进程控制吗?我们还需要一个类来描述子进程。因为我们需要知道父进程的每个写端的文件描述符和哪个子进程通过命名管道建立起来联系。 所以子进程类具有的属性就有了:

  1. 子进程的PID
  2. 写端的文件描述符
  3. 编号
  4. 名字 其中第3和第4个属性是为了方便父进程管理而加入的,后续进程控制类需要用数组将各个子进程存储起来。 子进程类代码如下:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class SubProcesses{
private:
    pid_t id_;
    int processNum_;
    int wfd_;
    std::string name_;
    static int cnt_;
public:
    SubProcesses(pid_t id,int wfd)
    :id_(id),processNum_(cnt_++),wfd_(wfd)
    {
        char buff[SIZE]{0};
        snprintf(buff,SIZE,"Process %d | pid:wfd [%d:%d]",processNum_,id_,wfd_);
        name_ = buff;
    }
    pid_t getId()
    {
        return id_;
    }
    std::string getName()
    {
        return name_;
    }
    int getWfd()
    {
        return wfd_;
    }
};
int SubProcesses::cnt_ = 1;

2.2 进程控制

那么进程类会具有什么属性呢?我们需要将创建出子进程进行管理可以用一个vecotr来对其进行管理,还需要知道我们需要创建的子进程数量,因为是基于命名管道的进程控制,为此我们还必须对个个进程管道进行管理,同样用vector将它们装起来。除此之外在来个权限掩码也可以。 如此进程控制类的成员变量:

  1. 管理着子进程的vector类。
  2. 管理着命名管道的vector类。
  3. 记录需要创建的子进程数目。
  4. 权限掩码。 下面开始实现函数:
2.2.2 创建子进程

创建子进程,创建子进程同样还是用到fork函数,不同的是不在需要pipe函数来创建管道了。现在已经变成了mkfifo函数。 和匿名管道那一样,我们要记得关闭子进程的写端,尽管子进程并没有主动打开写端,但是因为子进程继承父进程的缘故,依然会吧父进程的写端给继承过了的。一点定是要关闭的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void createProcess()
{
	std::vector<int> vfd;/*关闭子进程的写端*/
	for(int i = 1;i<=subProcessNum_;++i)
	{
		std::string fifoName = "fifo-"+std::to_string(i); 
		int ret = mkfifo(fifoName.c_str(),mode_);/*创建命名管道*/
		if(ret == -1)
		{
			perror("mkfifo");
			exit(1);
		}
		namePipe_.push_back(fifoName);/*存入命名管道*/
		pid_t id = fork();/*创建子进程*/
		if(id < 0)
		{
			perror("fork");
			exit(1);
		}
		else if(id == 0)
		{
			//子进程
			for(auto&fd:vfd)
			{
				close(fd);
			}
			int rfd = open(fifoName.c_str(),O_RDONLY);
			if(rfd == -1)
			{
				perror("open");
				exit(1);
			}
			//开始等待父进程的指令
			waitTask(rfd); /*子进程等待任务委派*/
			close(rfd);
			exit(0);
		}
		//父进程
		int wfd = open(fifoName.c_str(),O_WRONLY);
		if(wfd == -1)
		{
			perror("open");
			exit(1);
		}
		subProcess_.push_back({id,wfd});/*存入子进程信息*/
		fifoName.push_back(wfd);
	}
}
2.2.3 子进程等待任务

死循环等待任务,当写端关闭再退出。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void waitTask(int rfd)/*子进程等待任务委派*/
{
	while(true)
	{
		char buff[SIZE];
		int n = read(rfd,buff,SIZE);
		if(n == -1)
		{
			perror("read");
			exit(1);
		}
		else if(n>0)
		{
			Task().curTask((std::string)buff);
		}
		else if(n == 0)
		{
			/*证明写端没有写消息了*/
			std::cout<<"写端已经关闭,读端也即将关闭!"<<std::endl;
			break;
		}
	}
	
}
2.2.4 展示空闲进程
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*展示可选进程*/
    void showProcess()
    {
        std::cout<<"目前可用进程有:"<<std::endl;
        int i = 0;
        std::cout<<"|";
        for(auto&x:subProcess_)
        {
            std::cout<<"进程编号:"<<(i++)<<"进程PID:"<<x.getId()<<"| ";
        }
    }
2.2.5 分配任务给子进程

该函数的功能就分配任务,由用户自己选择子进程来执行任务。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*下达任务给子进程*/
void sendTask()
{
	std::cout<<"------------------------"<<std::endl;
	while(true)
	{
		int input = 0;
		do
		{
			showProcess();
			std::cout<<"请选择子进程#";
			std::cin>>input;   
		}while(input<0||input>=subProcessNum_);
		Task().showTask();
		std::string taskName;
		std::cout<<"请选择任务#";
		std::cin>>taskName;
		if(taskName == "exit")
		{
			break;
		}
		std::cout<<"选择进程-> "<<subProcess_[input].getName()<<" 执行"<<taskName<<" 任务"<<std::endl;
		write(subProcess_[input].getWfd(),taskName.c_str(),taskName.size());
		sleep(1);
	}
}
2.2.6 回收子进程

回收子进程的同时也不能忘记关闭文件描述符和关闭命名管道

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*关闭写端、删除文件、等待子进程退出*/
    void waitProcess()
    {
        for(int i = 0;i<subProcessNum_;++i)
        {
            close(subProcess_[i].getWfd());
            unlink(namePipe_[i].c_str());
            waitpid(subProcess_[i].getId(),nullptr,0);
        }
        std::cout<<"所有子进程已回收!"<<std::endl;
    }

代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unordered_map>
#include <string>
#include <fcntl.h>
#include <sys/stat.h>
#include <vector>
#include <map>
/**
 * 该文件为进程池的公共代码段
 * 进程池为利用命名管道实现的,简称命名管道进程池
 * 代码的主要功能包括:创建进程,控制进程实现委派给它的任务,等待进程
 */
#define SIZE 1024
typedef void(*task_t)(void);

//任务集
void printLog()
{
    std::cout<<"PID:"<<getpid()<<",正在打印日志..."<<std::endl;
}

void MySQLInsert()
{
    std::cout<<"PID"<<getpid()<<",正在将数据插入数据库..."<<std::endl;
}

void NetRequest()
{
    std::cout<<"PID"<<getpid()<<",正在请求网络..."<<std::endl;
}



class Task{
private:
    std::unordered_map<std::string,task_t> st_;
public:
    Task()
    {
        st_ = {{"打印日志",printLog},{"插入数据",MySQLInsert},{"请求网络",NetRequest}};
    }
    void showTask()/*展示任务*/
    {
        std::cout<<"可选任务:"<<std::endl;
        std::cout<<" {";
        for(auto&s:st_)
        {
            std::cout<<s.first<<' ';
        }
        std::cout<<'}'<<std::endl;
    }
    void curTask(const std::string& t)/*执行任务*/
    {
        if(st_.find(t) == st_.end())
        {
            std::cout<<"没有这个任务哦~"<<std::endl;
        }
        else
        {
            st_[t]();/*调用任务*/
        }
    }
};

class SubProcesses{
private:
    pid_t id_;
    int processNum_;
    int wfd_;
    std::string name_;
    static int cnt_;
public:
    SubProcesses(pid_t id,int wfd)
    :id_(id),processNum_(cnt_++),wfd_(wfd)
    {
        char buff[SIZE]{0};
        snprintf(buff,SIZE,"Process %d | pid:wfd [%d:%d]",processNum_,id_,wfd_);
        name_ = buff;
    }
    pid_t getId()
    {
        return id_;
    }
    std::string getName()
    {
        return name_;
    }
    int getWfd()
    {
        return wfd_;
    }
};
int SubProcesses::cnt_ = 1;

class ProcessCtrl{
private:
    std::vector<SubProcesses> subProcess_;/*子进程信息表*/
    std::vector<std::string> namePipe_;/*命名管道信息表*/
    int subProcessNum_ ;/*需要创建的子进程数目*/
    mode_t mode_ ;
public:
    ProcessCtrl(int subProcessNum = 3,mode_t mode = 0666)
    :subProcessNum_(subProcessNum),mode_(mode)
    {createProcess();/*开始创建子进程*/}
    void createProcess()
    {
        std::vector<int> vfd;/*关闭子进程的写端*/
        for(int i = 1;i<=subProcessNum_;++i)
        {
            std::string fifoName = "fifo-"+std::to_string(i); 
            int ret = mkfifo(fifoName.c_str(),mode_);/*创建命名管道*/
            if(ret == -1)
            {
                perror("mkfifo");
                exit(1);
            }
            namePipe_.push_back(fifoName);/*存入命名管道*/
            pid_t id = fork();/*创建子进程*/
            if(id < 0)
            {
                perror("fork");
                exit(1);
            }
            else if(id == 0)
            {
                //子进程
                for(auto&fd:vfd)
                {
                    close(fd);
                }
                int rfd = open(fifoName.c_str(),O_RDONLY);
                if(rfd == -1)
                {
                    perror("open");
                    exit(1);
                }
                //开始等待父进程的指令
                waitTask(rfd); /*子进程等待任务委派*/
                close(rfd);
                exit(0);
            }
            //父进程
            int wfd = open(fifoName.c_str(),O_WRONLY);
            if(wfd == -1)
            {
                perror("open");
                exit(1);
            }
            subProcess_.push_back({id,wfd});/*存入子进程信息*/
            fifoName.push_back(wfd);
        }
        
       
    }
    void waitTask(int rfd)/*子进程等待任务委派*/
    {
        while(true)
        {
            char buff[SIZE];
            int n = read(rfd,buff,SIZE);
            if(n == -1)
            {
                perror("read");
                exit(1);
            }
            else if(n>0)
            {
                Task().curTask((std::string)buff);
            }
            else if(n == 0)
            {
                /*证明写端没有写消息了*/
                std::cout<<"写端已经关闭,读端也即将关闭!"<<std::endl;
                break;
            }
        }
        
    }
    /*展示可选进程*/
    void showProcess()
    {
        std::cout<<"目前可用进程有:"<<std::endl;
        int i = 0;
        std::cout<<"|";
        for(auto&x:subProcess_)
        {
            std::cout<<"进程编号:"<<(i++)<<"进程PID:"<<x.getId()<<"| ";
        }
        std::cout<<std::endl;
    }
    /*下达任务给子进程*/
    void sendTask()
    {
        std::cout<<"------------------------"<<std::endl;
        while(true)
        {
            int input = 0;
            do
            {
                showProcess();
                std::cout<<"请选择子进程#";
                std::cin>>input;   
            }while(input<0||input>=subProcessNum_);
            Task().showTask();
            std::string taskName;
            std::cout<<"请选择任务#";
            std::cin>>taskName;
            if(taskName == "exit")
            {
                break;
            }
            std::cout<<"选择进程-> "<<subProcess_[input].getName()<<" 执行"<<taskName<<" 任务"<<std::endl;
            write(subProcess_[input].getWfd(),taskName.c_str(),taskName.size());
            sleep(1);
        }
    }
    /*关闭写端、删除文件、等待子进程退出*/
    void waitProcess()
    {
        for(int i = 0;i<subProcessNum_;++i)
        {
            close(subProcess_[i].getWfd());
            unlink(namePipe_[i].c_str());
            waitpid(subProcess_[i].getId(),nullptr,0);
        }
        std::cout<<"所有子进程已回收!"<<std::endl;
    }
};


int main()
{
    ProcessCtrl pp;
    pp.sendTask();
    return 0;
}

运行结果

运行结果
运行结果

3. 实时读取字符

我们还可以通过命名管道来实现字符的实时读取,还挺有意思的,为了实现这个功能,我们不仅需要会使用命名管道,还有如system()和fflush()函数。

3.1 公共区域

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdio>
#include <unistd.h>
#include <cstdlib>
#include <cassert>
#include <string>

#define SIZE 1024

//命名管道
const char* namePipe = "./fifo";
//权限掩码
const mode_t mode = 0666;

3.2 客户端

客户端用来向服务端发送消息

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 该文件为客户端,用来实时向服务端发送字符
 */

#include "common.hpp"

int main()
{
    /**主要步骤:
     * 1.创建命名管道
     * 2.打开命名管道
     * 3.写入字符到命名管道当中
     * 4.关闭命名管道   
     * */
    int mk = mkfifo(namePipe,mode);
    if(mk == -1)
    {
        perror("mkfifo");
        exit(1);
    }
    int fd = open(namePipe,O_WRONLY);//写方式打开
    if(fd == -1)
    {
        perror("open");
        exit(1);
    }
    std::cout<<"开始输入字符:ctrl+c退出"<<std::endl;
    while(true)
    {
        system("stty raw");/*调用 shell 命令 `stty raw` 的,它的作用是将终端设置为 原始模式(raw mode)。*/
        int c = getchar();
        system("stty -raw");/*用于将终端从 原始模式(raw mode) 恢复到 规范模式(cooked mode)。*/
        if(c == 3)/*ctrl+c == 3 */
        {
            std::cout<<"exit!"<<std::endl;
            break;
        }
        ssize_t n = write(fd,(char*)&c,sizeof(char));
        assert(n>=0);
        (void)n;/*消除未使用警告*/
    }
    close(fd);
    unlink(namePipe);
    return 0;
}

3.3 服务端

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 该文件为服务端,用来实时接受客户端输出的字符
 */


#include "common.hpp"

int main()
{
    /**
     * 实时读取客户端发来的字符
     * 主要功能:
     * 
     * 1.已读方式打开命名管道文件
     * 2.利用fflush实时刷新缓冲区的字符
     * 3.关闭文件描述符
     */
    int fd = open(namePipe,O_RDONLY);
    assert(fd!=-1);
    while(true)
    {
        char buff[SIZE]{0};
        ssize_t n = read(fd,buff,SIZE-1);
        if(n>0)
        {
            buff[n] = 0;
            printf("%c",buff[0]);
            fflush(stdout);
        }
        else if(n == 0)
        {
            std::cout<<std::endl;
            std::cout<<"写端退出,终止读端"<<std::endl;
            break;
        }
        else 
        {
            perror("read");
            close(fd);
            exit(1);
        }
    }
    close(fd);
    return 0;
}

效果图

屏幕录制 2024-11-20 202850

往期Linux文章:Linux专栏

4.总结

通过命名管道实现了这两个简单的小程序,其实这些小程序的本质都是一样的:创建命名管道 -> 打开命名管道 -> 通信 -> 关闭命名管道,掌握其中一个即可融会贯通! 感谢阅读。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Webpack 5新特性详解与性能优化实践
Webpack 5通过确定性的Chunk ID、模块ID和导出ID实现了长期缓存,这意味着相同的输入将始终产生相同的输出。这样,当你的用户再次访问更新后的网站时,浏览器可以重用旧的缓存,而不是重新下载所有资源。
天涯学馆
2024/05/18
2990
前端工程化之Webpack优化
好久没更文了,其实这段时间,一直没闲着。在准备一些比较重要的东西。忙着跑步,忙着学习,忙着xx。 总之就是,一直在忙着,从未停歇。
前端柒八九
2022/09/16
1.2K2
webpack5快发布了,你还没用过4吗?
webpack5 预计会在 2020 年年初发布,之前从 alpha 版本就有关注,本次重点更新在长期缓存,tree shakking 和 es6 打包这块。具体变更可以参考https://github.com/webpack/changelog-v5/blob/master/README.md。
前端森林
2020/04/23
1.7K0
webpack5快发布了,你还没用过4吗?
Webpack5 实践 - 构建效率倍速提升!
对于前端构建工具 Webpack、babel、eslint 等的每一次升级,就像刚刚经历一场地震似得,最不想面对的就是处理各种 API 的不兼容性,有时还会出现一些奇奇怪怪的问题,为什么还要升呢?并不是为了给自己找事,还是要讲究投入产出比的,也就是最终的收益是要大于产出比的。
五月君
2021/07/15
3K0
Webpack5 实践 - 构建效率倍速提升!
webpack配置完全指南
对于入门选手来讲,webpack 配置项很多很重,如何快速配置一个可用于线上环境的 webpack 就是一件值得思考的事情。其实熟悉 webpack 之后会发现很简单,基础的配置可以分为以下几个方面: entry 、 output 、 mode 、 resolve 、 module 、 optimization 、 plugin 、 source map 、 performance 等,本文就来重点分析下这些部分。
gogo2027
2022/09/26
3.3K0
webpack5学习笔记
assetModuleFilename: 'images/contenthash.png'
代码哈士奇
2022/01/26
2.7K0
构建 webpack5 知识体系【近万字总结】
我持续组织了近一年的源码共读活动,感兴趣的可以 点此扫码加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列。另外:目前建有江西|湖南|湖北籍前端群,可加我微信进群。
若川
2022/11/11
1.6K0
构建 webpack5 知识体系【近万字总结】
Webpack Bundle Analyzer:深入分析与优化你的包
Webpack Bundle Analyzer是一个用于可视化的工具,它可以帮助你分析Webpack打包后的输出文件,查看哪些模块占用了最多的空间,从而进行优化。
天涯学馆
2024/08/19
6550
Webpack5 新特性业务落地实战
Webpack5 在 2020 年 10 月 10 日正式发布,并且在过去的几个月中快速演进和迭代,截止 1 月 28 日,Webpack5 已经更新了 18 个 minor 版本,带来了许多十分吸引人的新特性。据官网介绍[1],Webpack5 整体的方向性变化有以下几点:
前端迷
2021/03/18
1.4K0
Webpack5 新特性业务落地实战
梳理 6 项 webpack 的性能优化
webpack在启动后,会根据Entry配置的入口,递归解析所依赖的文件。这个过程分为「搜索文件」和「把匹配的文件进行分析、转化」的两个过程,因此可以从这两个角度来进行优化配置。
Nealyang
2020/07/24
2K0
webpack 学习笔记系列06-打包优化
可选值:async(默认) | initial | all(推荐),针对下面的 a.js 和 b.js
CS逍遥剑仙
2021/06/27
2K0
webpack的进阶使用
Webpack 是一个模块打包器,可以将多个模块打包成一个或多个文件。除了基本的打包功能外,Webpack 还提供了很多高级功能,可以优化打包结果、提高构建速度等。在本文中,我们将介绍 Webpack 的一些进阶使用技巧,并提供相应的代码示例。
世间万物皆对象
2024/03/20
1240
使用Webpack5创建Vue2项目及优化
之前我们大多都是用Vue-Cli来创建项目,但是Vue-Cli已经停止更新了,并且Vue-Cli相当于一堆插件的集合体,我们想替换以下,或者想根据我们的项目优化以下,提升编译的性能,这时候可以自己用Webpack来配置项目。
码客说
2022/09/27
3.2K0
使用Webpack5创建Vue2项目及优化
Webpack最佳实践
Webpack 可以看做是模块打包机,把解析的所有模块变成一个对象,然后通过入口模块去加载我们的东西,然后依次实现递归的依赖关系,通过入口来运行所有的文件。由于 webpack 只认识js,所以需要通过一系列的 loader 和 plugin 转换成合适的格式供浏览器运行。
gogo2027
2022/10/17
3.4K0
从零搭建一个 webpack 脚手架工具(三)
配置了那么多,优化处理却很少,特别是导出的文件只有一个,这样会让文件非常大,这时候就需要切片处理以及分离文件。
多云转晴
2019/12/26
1.5K0
万字梳理 Webpack 常用配置和优化方案
在项目根目录下新建 webpack.config.js,作为 webpack 的默认配置文件。
Chor
2021/09/08
3K0
爆肝总结万字长文笔记webpack5打包资源优化
webpack是如何打包资源优化,你有了解吗?或者一个经常被问的面试题,首屏加载如何优化,其实无非就是从http请求、文件资源、图片加载、路由懒加载、预请求,缓存这些方向来优化,通常在使用脚手架中,成熟的脚手架已经给你做了最大的优化,比如压缩资源,代码的tree shaking等。
Maic
2022/07/28
2K0
爆肝总结万字长文笔记webpack5打包资源优化
webpack 基础知识整理
webpack是一个 模块打包工具,支持所有的打包语法,比如 ES Module、CommonJS、CMD、AMD。初期的webpack是用来模块打包js的,发展到现在,已经可以打包很多种文件类型,比如 css、img 。
神葳
2021/01/22
1.4K0
webpack的高阶使用
Webpack 是一款强大的模块打包工具,广泛应用于现代前端开发中。本文将从以下几个方面讨论 Webpack 的高阶使用方法:
世间万物皆对象
2024/03/20
1430
webpack性能优化总结大全
由于 Loader 对文件的转换操作很耗时,所以需要让尽可能少的文件被 Loader 处理。可以通过 test/include/exclude 三个配置项来命中 Loader 要应用规则的文件。
前端迷
2019/10/22
1.8K0
相关推荐
Webpack 5新特性详解与性能优化实践
更多 >
LV.3
这个人很懒,什么都没有留下~
目录
  • 1. 进程池
  • 2. 进程池的功能
    • 2.1 可被执行的任务
    • 2.2 进程控制(重点)
      • 2.2.1 子进程类
    • 2.2 进程控制
      • 2.2.2 创建子进程
      • 2.2.3 子进程等待任务
      • 2.2.4 展示空闲进程
      • 2.2.5 分配任务给子进程
      • 2.2.6 回收子进程
  • 代码
    • 运行结果
  • 3. 实时读取字符
    • 3.1 公共区域
    • 3.2 客户端
    • 3.3 服务端
  • 效果图
  • 4.总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档