Linux环境,C/C++语言手写代码实现线程池

Linux - C++实现简单线程池 Linux下,用C++实现简单线程池。线程安全的单例模式 阅读详情

前言

在我们日常生活中会遇到许许多多的问题,如果一个服务端要接受很多客户端的数据,该怎么办?多线程并发内存不够怎么办?所以我们需要了解线程池的相关知识。

一、线程池是什么?

1.线程池的简介

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:C/C++ ,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,点击立即学习

2.线程池的组成

1、线程池管理器(ThreadPoolManager):用于创建并管理线程池

2、工作线程(WorkThread): 线程池中线程

3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行。

4、任务队列:用于存放没有处理的任务。提供一种缓冲机制。

3.线程池的主要优点

1.避免线程太多,使得内存耗尽

2.避免创建与销毁线程的代价

3.任务与执行分离

二、代码实现

1.线程池结构体定义

代码如下(示例):

struct nTask {

	void (*task_func)(struct nTask* task);
	void* user_data;
	struct nTask* prev;
	struct nTask* next;
};


struct nWorker {
	pthread_t threadid;
	int terminate;
	struct nManager* manager;
	struct nWorker* prev;
	struct nWorker* next;
};


typedef struct nManager {
	struct nTask* tasks;
	struct nWorker* workers;
	pthread_mutex_t mutex;//定义互斥锁
	pthread_cond_t cond;//条件变量
}ThreadPool;

2.接口定义

代码如下(示例):

//API
int nThreadPoolCreate(ThreadPool* pool, int numWorkers) {

	if (pool == NULL) return -1;
	if (numWorkers < 1) numWorkers = 1;
	memset(pool, 0, sizeof(ThreadPool));

	pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
	memcpy(&pool->cond, &blank_cond, sizeof(pthread_cond_t));

	pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
	memcpy(&pool->mutex, &blank_mutex, sizeof(pthread_mutex_t));

	int i = 0;
	for (i = 0; i < numWorkers; i++) {
		struct nWorker* worker = (struct nWorker*)malloc(sizeof(struct nWorker));
		if (worker == NULL) {
			perror("malloc");
			return -2;
		}
		memset(worker, 0, sizeof(struct nWorker));
		worker->manager = pool;

		int ret = pthread_create(&worker->threadid, NULL, nThreadPoolCallback, worker);
		if (ret) {
			perror("pthread_create");
			free(worker);
			return -3;
		}
		LIST_INSERT(worker, pool->workers);

	}

	return 0;
}

//API
int nThreadPoolDestory(ThreadPool* pool, int nWorker) {

	struct nWorker* worker = NULL;

	for (worker = pool->workers; worker != NULL; worker = worker->next) {
		worker->terminate;
	}

	pthread_mutex_lock(&pool->mutex);

	pthread_cond_broadcast(&pool->cond);//唤醒所有线程

	pthread_mutex_unlock(&pool->mutex);

	pool->workers = NULL;
	pool->tasks = NULL;

	return 0;

}

//API
int nThreadPoolPushTask(ThreadPool* pool, struct nTask* task) {

	pthread_mutex_lock(&pool->mutex);

	LIST_INSERT(task, pool->tasks);

	pthread_cond_signal(&pool->cond);//唤醒一个线程

	pthread_mutex_unlock(&pool->mutex);

}

3.回调函数

代码如下(示例):

static void* nThreadPoolCallback(void* arg) {

	struct nWorker* worker = (struct nWorker*)arg;

	while (1)
	{
		pthread_mutex_lock(&worker->manager->mutex);
		while (worker->manager->tasks == NULL) {
			if (worker->terminate)break;
			pthread_cond_wait(&worker->manager->cond, &worker->manager->mutex);
		}

		if (worker->terminate) {
			pthread_mutex_unlock(&worker->manager->mutex);
			break;
		}

		struct nTask* task = worker->manager->tasks;
		LIST_REMOVE(task, worker->manager->tasks);

		pthread_mutex_unlock(&worker->manager->mutex);

		task->task_func(task);
	}

	free(worker);
	return;
}

4.全部代码(加注释)

代码如下(示例):

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>//Linux编程使用线程相关的就会用到这个,编译的时候还要加-lpthread,用pthread动态库


#define LIST_INSERT(item,list) do{	\			//链表的增加
		item->prev = NULL;			\
		item->next = list;			\
	if((list)!=NULL)(list)->prev=item;	\
		(list) = item;				\
}while(0)


#define LIST_REMOVE(item,list)do{	\			//链表的删除
		if (item->prev != NULL) item->prev->next = item->next;	\
		if (item->next != NULL) item->next->prev = item->prev;	\
		if (item == list) list = item->next;					\
		item->prev = item->next = NULL;							\
}while(0)




struct nTask {		//任务

	void (*task_func)(struct nTask* task);
	void* user_data;

	struct nTask* prev;
	struct nTask* next;
};


struct nWorker {	//工作
	pthread_t threadid;
	int terminate;	//用来判断工作是否结束,以便于终止
	struct nManager* manager;	//可以用来查看任务

	struct nWorker* prev;
	struct nWorker* next;
};


typedef struct nManager {	//管理
	struct nTask* tasks;
	struct nWorker* workers;

	pthread_mutex_t mutex;//定义互斥锁,也可以用自旋锁
	pthread_cond_t cond;//条件变量
}ThreadPool;

//callback!=task
static void* nThreadPoolCallback(void* arg) {

	struct nWorker* worker = (struct nWorker*)arg;	//接受pthread_create传来的参数

	while (1)	//默认一直执行
	{
		pthread_mutex_lock(&worker->manager->mutex);//加锁
		while (worker->manager->tasks == NULL) {
			if (worker->terminate)break;
			pthread_cond_wait(&worker->manager->cond, &worker->manager->mutex);//如果没有任务就等待
		}

		if (worker->terminate) {
			pthread_mutex_unlock(&worker->manager->mutex);//解锁
			break;
		}

		struct nTask* task = worker->manager->tasks;
		LIST_REMOVE(task, worker->manager->tasks);

		pthread_mutex_unlock(&worker->manager->mutex);

		task->task_func(task);
	}

	free(worker);
	return;
}


//API
int nThreadPoolCreate(ThreadPool* pool, int numWorkers) {

	if (pool == NULL) return -1;
	if (numWorkers < 1) numWorkers = 1;//如果任务数量小于1,就默认为1
	memset(pool, 0, sizeof(ThreadPool));

	pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;//创建互斥量cond,静态全局
	memcpy(&pool->cond, &blank_cond, sizeof(pthread_cond_t));

	//pthread_mutex_init(&pool->mutex, NULL);
	pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;//创建互斥锁,静态全局
	memcpy(&pool->mutex, &blank_mutex, sizeof(pthread_mutex_t));

	int i = 0;
	for (i = 0; i < numWorkers; i++) {
		struct nWorker* worker = (struct nWorker*)malloc(sizeof(struct nWorker));
		if (worker == NULL) {
			perror("malloc");
			return -2;
		}
		memset(worker, 0, sizeof(struct nWorker));
		worker->manager = pool;

		int ret = pthread_create(&worker->threadid, NULL, nThreadPoolCallback, worker);
		if (ret) {
			perror("pthread_create");
			free(worker);
			return -3;
		}
		LIST_INSERT(worker, pool->workers);
	}

	return 0;
}

//API
int nThreadPoolDestory(ThreadPool* pool, int nWorker) {

	struct nWorker* worker = NULL;

	for (worker = pool->workers; worker != NULL; worker = worker->next) {
		worker->terminate;
	}

	pthread_mutex_lock(&pool->mutex);

	pthread_cond_broadcast(&pool->cond);//唤醒所有线程

	pthread_mutex_unlock(&pool->mutex);

	pool->workers = NULL;//置空
	pool->tasks = NULL;//置空

	return 0;

}

//API
int nThreadPoolPushTask(ThreadPool* pool, struct nTask* task) {

	pthread_mutex_lock(&pool->mutex);

	LIST_INSERT(task, pool->tasks);

	pthread_cond_signal(&pool->cond);//唤醒一个线程

	pthread_mutex_unlock(&pool->mutex);

}




#if 1

#define THREADPOOL_INIT_COUNT	20
#define TASK_INIT_SIZE			1000


void task_entry(struct nTask* task) {

	int idx = *(int*)task->user_data;

	printf("idx: %d\n", idx);

	free(task->user_data);
	free(task);
}


int main(void) {

	ThreadPool pool = { 0 };
	nThreadPoolCreate(&pool, THREADPOOL_INIT_COUNT);

	int i = 0;
	for (i = 0; i < TASK_INIT_SIZE; i++) {
		struct nTask* task = (struct nTask*)malloc(sizeof(struct nTask));
		if (task == NULL) {
			perror("malloc");
			exit(1);
		}
		memset(task, 0, sizeof(struct nTask));

		task->task_func = task_entry;
		task->user_data = malloc(sizeof(int));
		*(int*)task->user_data = i;


		nThreadPoolPushTask(&pool, task);
	}

	getchar();//防止主线程提前结束任务

}


#endif
总结

总结

关于线程池是基本代码就在上面了,关于编程这一部分内容,我建议大家还是要自己去动手实现,如果只是单纯的看了一遍,知识这块可能会记住,但是操作起来可能就比较吃力,万事开头难,只要坚持下去,总会有结果的。

参考资料

C/C++ Linux后台高级开发

内容包括C++工程化、高性能及分布式、深入浅出。性能调优、TCP,协程,Nginx源码分析Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,Linux内核,P2P,K8S,Docker,TCP/IP,协程,DPDK多个高级知识点。

以下视频学习资料获取点击:C++架构师学习资料

​

 

Linux c/c++环境编程 本文摘要: 第三章对比了互斥锁、自旋锁和原子操作的区别,重点介绍了CAS原子操作的实现原理和应用场景,并提供了互斥锁的代码示例。 第四章详细讲解了线程池的实现,包括任务队列管理、线程创建与任务执行流程,通过代码展示了线程池的初始化、任务分配和执行过程。 第五章探讨了MySQL数据库操作,包括图片存储的实现方法,对比了普通数据插入与BLOB类型数据处理的差异,提出了关于数据库连接池的设计思考。文章通过具体代码示例演示了从磁盘读取图片、写入数据库以及反向操作的完整流程,并分析了相关技术难点。 阅读详情

相关推荐

Linux下分别使用C和C++语言实现线程池(附带源码和解析)

本文记录了分别使用C和C++语言实现线程池的过程,并附带源码和解析。 一、线程池理论知识 1.1什么是线程池 线程池(thread pool): 一种线程使用模式,线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在短时间任务创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数据取决于可用的并发处理器、处理器内核、内存、网络sockets等数量。 因为程序边运行边创建线程是比较耗时的,所以我们通过池化的

AnChenliang_1002的博客 711

线程池实现简单案例(C语言)

C语言简单实现线程池案例

向往天空的鱼 1544

Linux线程池的C语言实现指南

线程池是由一个或多个可重用的线程组成的池,这些线程在池中等待分配任务。当一个新任务提交给线程池时,它不会立即创建一个新的线程来处理该任务。相反,它会将任务分配给池中已存在的线程,或者如果当前没有空闲线程,则将任务加入队列等待。当一个线程完成它的任务后,它会返回到线程池中并准备接受新的任务。在软件架构中,线程池的工作原理如下:初始化:线程池创建一定数量的线程,这些线程处于等待任务的状态。任务提交:当有新任务提交时,线程池首先检查是否有空闲线程。如果有,则将任务直接分配给空闲线程执行。

weixin_28721743的博客 918

MCU缺货涨价背后:国产替代的真实水位与实操路径

在嵌入式系统开发中,MCU作为核心控制器,其供应链稳定性直接影响产品交付。2020年以来的缺货潮暴露了依赖单一国际大厂的系统性风险,也加速了国产MCU的导入进程。从ARM Cortex-M内核的生态兼容性到工具链迁移,从外设驱动适配到低功耗优化,国产芯片已在消费电子和部分工业场景实现闭环替代,但在模拟精度、车规认证和生态成熟度上仍需爬坡。对于硬件工程师和项目管理者而言,理解替代成熟度分级、掌握选型评估与代码迁移方法,是构建供应链韧性的关键。本文基于真实项目复盘,梳理国产MCU从选型到量产的完整路径与避坑指南

weixin_34117211的博客 431

Linux下C语言实现简单线程池:核心原理与代码解析

多线程编程中,频繁创建和销毁线程会带来巨大的系统开销,严重制约高并发服务的吞吐能力。线程池通过预先创建并复用线程资源,将任务分发与线程生命周期解耦,是解决并发场景下资源消耗问题的关键设计模式。其核心机制依赖互斥锁与条件变量的协同,配合任务队列完成生产者-消费者模型,实现高效的并发控制。目前该技术在Web服务器、网关及嵌入式系统等高并发应用中有着广泛的实践价值。在Linux环境下,使用C语言和POSIX线程接口可以深入展示线程池的底层细节,包括线程数组、锁、条件变量以及动态扩缩容策略。本文剖析一个基于Linu

weixin_30843121的博客 277

Linux C++ 服务器之线程池 C++11

基于Linux平台,C++开发,socket服务器之线程池,使用thread,同步利用mutex和condition_variable实现

linux下实现一个C++线程池

C++练手,在linux环境下实现一个线程池

weixin_45157276的博客 1420

linux C++ 实现线程池(避免线程创建的耗时)

linux下c++写的线程池,可以了解pthread_cond_timewait和pthread_detach的用法,自定义最大使用的线程数量,线程退出线程池的超时时间,任务优先级处理。

Linux中基于c/c++多线程编程学习杂记:c语言手写线程池

文章转载自爱编程的大丙

m0_68731112的博客 615

RISC-V— 架构基础知识学习

蜂鸟 E200 系列是一个开源的RISC-V 处理器。蜂鸟 E200 系列由中国大陆研发团队开发,用户能够轻松与开发人员取得交流和支持。RI蜂鸟 E200 处理器研发团队拥有在国际一流公司多年开发处理器的经验,使用稳健的Verilog 2001 语法编写的可综合 RTL 代码,以工业级标准进行开发。蜂鸟 E200 的代码为人工编写,添加丰富的注释且可读性强,非常易于理解。

deilt 1万+

流量特征工程实战:基于Python脚本的CIC-FlowMeter批量自动化处理

本文详细介绍了如何利用Python脚本对CIC-FlowMeter工具进行批量自动化封装,实现从海量PCAP文件中高效提取流量特征并生成CSV文件。通过构建健壮的自动化流水线,解决了手动处理效率低下、易出错的问题,并分享了并行处理、错误处理等关键优化技巧,助力网络流量分析与机器学习数据预处理工作。

c6d7e8f9g的博客 745

Linux C/C++ 之 线程池

linux 线程池

m0_73588724的博客 811

通俗理解Xgboost

为了简化计算,XGBoost采用了一阶导数(梯度)和二阶导数(Hessian)来近似损失函数的变化,从而推导出叶子节点值的更新公式。其中 z 就是所有树的叶子节点分数之和。通过这两个正则化项,XGBoost能够在优化目标函数时,不仅考虑损失函数的最小化,还考虑了模型的复杂度,从而达到更好的泛化性能。正则化项通常包括两个部分:一个是与叶子节点数量相关的项,另一个是与叶子节点输出值大小相关的项。通过这种方式,XGBoost能够有效地计算出每个叶子节点的值,同时通过正则化项来平衡模型的复杂度和泛化能力。

金融风控笔记 755

Linux下c语言的线程池(分模块讲解)

关于C语言线程池的理解(包含全部的模块以及一个小测试)

记录一位C++爱好者的笔记 1336

【无人机对抗】基于matlab无人机红蓝对抗仿真【含Matlab源码 4436期】

无人机红蓝对抗仿真 完整代码,包运行;可提供运行操作视频!适合小白!

Matlab3024528700的博客 1383

37、半导体硅材料中的缺陷与吸杂技术解析

本文系统解析了半导体硅材料中的缺陷形成机制及其对电子器件性能的影响,重点探讨了生长和工艺诱导缺陷、表面与内部微缺陷的起源与特征。文章详细阐述了吸杂技术的原理、过程及在集成电路和太阳能电池制造中的应用,强调通过氧行为调控和热处理优化实现缺陷控制。同时介绍了吸杂效果的评估方法,并展望了未来新型吸杂技术的发展趋势,为提升半导体器件性能和制造良率提供理论支持和技术路径。

mongodb5scout的博客 164

Linux环境下实现并详细分析c/cpp线程池(附源码)

Linux环境下实现C/CPP线程池

拉依达不拉跨的博客 1263

突破Cloudflare 5秒盾:Python爬虫+GPT分析JS混淆,零基础也能爬

传统突破Cloudflare 5秒盾的门槛极高,需要精通JS逆向、浏览器指纹伪造、行为模拟,而本文的“Playwright+GPT”方案,彻底降低了门槛——无需懂逆向、无需手动改指纹、无需写复杂JS代码,只需3步就能突破大部分网站的防护。低门槛:新手1小时内就能上手,无需逆向工程知识;高成功率:覆盖80%+的普通防护网站,15%的中等防护网站;易扩展:适配不同类型网站,可结合代理、打码平台升级为高可用系统。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1165

linux的C/C++线程池(VS2019开发)

启动测试:VS2019运行Linux程序报错:无法启动gdb。任务队列,存储需要处理的任务,由工作的线程来处理这些任务。工作的线程(任务队列任务的消费者) ,N个。管理者线程(不处理任务队列中的任务),1个。连接linux虚拟机。

红叶谷 wsp_1138886114的博客 1126

基于YOLOv5的人脸识别系统:从构建到应用的全流程指南

本文详细介绍了基于YOLOv5的人脸识别系统开发全流程。首先阐述了YOLOv5模型的高效性和准确性优势,随后分步骤讲解了深度学习环境配置、数据集准备(推荐WIDER FACE数据集)和模型训练方法。重点展示了如何将训练好的模型集成到Tkinter开发的GUI界面中,实现图片上传、人脸检测和结果可视化功能。文章提供完整的代码示例和飞书文档链接(密码:946m22&8),帮助开发者快速构建可交互的人脸识别系统,适用于安防、门禁等多种应用场景。通过该项目,读者可掌握YOLOv5的实际应用和界面集成技术。

FJN110的博客 169

2025资源从哪里来!

研究网络通信中的安全问题,如VLAN划分、路由协议(静态、动态如OSPF、BGP)、NAT转换、AAA认证、访问控制列表(ACL)等。‌Wi-Fi安全设置‌、‌密码破解原理‌(仅用于合法测试或教育目的)、‌常见Web安全问题‌如SQL注入、XSS跨站脚本攻击等的防范措施。在这个网站你可以找到你对应的专业-对应的路线-对应的资源-对应的百度网盘里面全是免费的资源------------------惊喜不?‌防火墙配置与策略‌、‌入侵检测与防御系统(IDS/IPS)‌、‌端口扫描与漏洞扫描技术‌等。

qq_56248592的博客 1907

C++程序员内功修炼——Linux C/C++编程技术汇总

在软件开发的宏大版图中,C++ 语言宛如一座巍峨的高山,吸引着无数开发者攀登探索。而 Linux 操作系统,以其开源、稳定、高效的特性,成为了众多开发者钟爱的开发平台。将 C++ 与 Linux 相结合,就如同为开发者配备了一把无坚不摧的利刃,能够在系统级编程、高性能计算、游戏开发等诸多领域披荆斩棘。对于 C++ 程序员而言,掌握 Linux 环境下的 C/C++ 编程技术,不仅仅是提升自身技能的关键一步,更是在激烈的职场竞争中脱颖而出的必备法宝。

深度Linux 2681

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Linux环境下的C/C++开发学习之旅

在当今软件开发领域,C和C++作为底层与系统级编程的重要语言,在Linux操作系统环境下具有极高的应用价值。在学习过程中,动手实现一些小型项目,如文件复制程序、简单的HTTP服务器、多线程并发任务调度器等,可以帮助你巩固所学知识,并对Linux环境下的C/C++开发有更深入的理解。:掌握Linux下的套接字编程,包括TCP/IP协议栈的理解,socket API的使用,以及并发服务器模型的设计。:C++引入了类、对象、继承、封装、多态等面向对象特性,这是C++区别于C的重要部分,需要深入理解和熟练运用。

LinuxTaoist的博客 771

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

c/c++linux后台服务器开发如何提升?(路线图已备好)

随着业务市场的不断壮大,更便捷的开发语言也越来越受到市场的欢迎。Java,python还有新贵golang。 那c/c++语言的开发者市场在哪里。虽然说没有活干说的可能过于夸张,但是面临的事实就是比不了。可能初学一点Java,python等等就能找到一份不错的开发工作,但是如果你只会一点c,又不是校招的话,能找到哪些工作呢? C,c++不同于Java。Java的技术路线是比较固定的,学好技术栈...

资料qun832218493 1万+

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

终极指南:如何用C/C++高效运行大语言模型llama.cpp

想要在本地高效运行大语言模型,又不想依赖复杂的Python环境?llama.cpp正是你需要的解决方案!这个用纯C/C++编写的大语言模型推理框架,让你能在各种硬件上实现高性能的LLM推理,从个人电脑到移动设备,再到服务器集群。无论你是开发者、研究人员还是技术爱好者,掌握llama.cpp都能让你在AI应用开发中占据先机。 ## 🚀 项目哲学:极简主义的AI推理引擎 llama.cpp的核心

gitblog_01154的博客 453

【宠粉赠书】Linux C与C++一线开发实践

为了回馈粉丝们的厚爱,今天小智给大家送上一套Linux开发学习的必备书籍——《Linux C与C++一线开发实践》。《Linux C与C++一线开发实践》贴近软件公司一线开发实践,系统讲解Linux C/C++网络应用开发方法。全书内容针对初中级读者,假设读者没有接触过C/C++编程和Linux系统开发,任何立志成为一名Linux开发工程师的人,都可以从本书起步。全书厚达620多页,内容通俗易懂,由浅入深,并且实例丰富、步骤详细、注释充分,能帮助读者快速入门。

踏雨歌青春,诗酒趁年华 5844
上一篇: 后端开发—一文详解网络IO模型
下一篇: 一文掌握tcp服务器epoll的多种实现
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值