【C/C++】C语言多线程开发 —— 线程池

春招 all in C++

H i , a l l   I   a m   c o m i n g ! Hi, all\ I\ am\ coming! Hi,all I am coming!

重新找到了半夜写 C S D N CSDN CSDN 的感觉

C语言到了进阶路上之后, 就需要面对多线程开发了

以下是我在 八 股 八股 的过程中, 准备的多线程的优点

  • 提高 CPU 的利用率
  • 线程的创建开销远比进程要小很多, 所以切换时保存上下文的速度会快很多

同时多线程的缺点

  • 稳定性低, 不可靠, 某个线程的瘫痪、可能影响整个进程
  • 安全性要求高, 由于对同一个进程的资源进行访问, 那么必然会涉及到锁的开销
  • 缓存行对齐 (局部性原理)

众所周知, 对于每一个进程来说32位操作系统虚拟地址 3 G, 每个线程创建需要 8MB, 32位系统上理论上可以创建 3G/8MB 个线程, 但实际上远比这小很多(一个进程又不是全是线程在支撑, 还有进程所包含的资源)

如果此时假设需要输出 1000000000000 个数字, 如果用一个线程可能执行需要很久, 两个时间就会缩短一些, 同样的越多的线程时间肯定会越短, 但是并不是线程越多越好, 理论上来说, 除了创建线程的数量有上限以外, 同时还包括锁的开销, 多个线程竞争资源的时候开销很大

所以需要一个线程池, 线程池的作用:

  • 起到缓冲作用, 在线程 与 任务分配之间起到缓冲作用
  • 提升复用性, 减少线程创建的开销

具体代码如下


#include <pthread.h>
#include <string.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h> 
// 增加
#define LIST_ADD(item, list) do {\
	item -> prev = NULL;	\
	item -> next = list;	\
	if (list) {				\
		list -> prev = item;\
	}						\
	list = item;			\
} while(0)

// 删除
#define LIST_DELETE(item, list) do {\
	if (item -> prev) {\
		item -> prev -> next = item -> next;\
	}\
	if (item -> next) {\
		item -> next -> prev = item ->prev;\
	}\
	if (item == list) {\
		list = item -> next;\
	}\
	item -> prev = item -> next = nullptr;\
} while(0)

struct THREAD_POOL_WOKER {

	pthread_t thread;
	
	struct THREAD_MANAGER *pool;
	
	int terminate; // 停止标识符

	struct THREAD_POOL_WOKER* next;
	struct THREAD_POOL_WOKER* prev;
};

struct THREAD_POOL_JOB {
	void (*func)(pthread_t &thread, void *user_data);
	void* user_data;
	
	struct THREAD_POOL_JOB* next;
	struct THREAD_POOL_JOB* prev;
};

struct THREAD_MANAGER {
	struct THREAD_POOL_WOKER* workers;
	struct THREAD_POOL_JOB* jobs;
	pthread_cond_t jobs_cond;
	pthread_mutex_t jobs_mutex;	
};

static void* callback(void* arg) {

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

	while (1) {
		// enter
		pthread_mutex_lock(&worker->pool->jobs_mutex);

		while (worker->pool->jobs == nullptr) {
			if (worker->terminate) {
				break;
			}
			pthread_cond_wait(&worker->pool->jobs_cond, &worker->pool->jobs_mutex);
		}

		if (worker->terminate) {
			pthread_mutex_unlock(&worker->pool->jobs_mutex);
			break;
		}

		struct THREAD_POOL_JOB *job = worker->pool->jobs;

		LIST_DELETE(job, worker->pool->jobs);

		pthread_mutex_unlock(&worker->pool->jobs_mutex);

		job->func(worker->thread, job->user_data);
	}

	free(worker);

	pthread_exit(NULL);

}

int THreadPoolCreate(struct THREAD_MANAGER *pool, int workNumbers) {
	if (workNumbers < 1) {
		workNumbers = 1;
	}
	if (pool == NULL) {
		return -1;
	}

	memset(pool, 0, sizeof(THREAD_MANAGER));

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


	pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
	memcpy(&pool->jobs_mutex, &blank_mutex, sizeof(pthread_mutex_t));
 
	for (int i = 0; i < workNumbers; i++) {
		struct THREAD_POOL_WOKER *worker = (struct THREAD_POOL_WOKER*)malloc(sizeof(THREAD_POOL_WOKER));
		if (worker == NULL) {
			printf("details : malloc error\n");
			return -1;
		}
		memset(worker, 0, sizeof(struct THREAD_POOL_WOKER));

		int ret = pthread_create(&worker->thread, NULL, callback, worker);

		if (ret) {
			printf("details : thread error\n");
			return -2;
		}

		// work 
		worker->pool = pool;

		LIST_ADD(worker, pool->workers);
	}

	return 0;

}

void THreadPoolDestory(struct THREAD_MANAGER* pool) {
	struct THREAD_POOL_WOKER *work = nullptr;

	for (work = pool->workers; work != nullptr; work = work->next) {
		work->terminate = 1;
	}

	pthread_mutex_lock(&pool->jobs_mutex);
	pthread_cond_broadcast(&pool->jobs_cond);
	pthread_mutex_unlock(&pool->jobs_mutex);
}


void THreadPoolPushJob(struct THREAD_MANAGER* pool, struct THREAD_POOL_JOB* job) {
	
	pthread_mutex_lock(&pool->jobs_mutex);
	// 临界区
	LIST_ADD(job, pool->jobs);
	
	pthread_cond_signal(&pool->jobs_cond);

	pthread_mutex_unlock(&pool->jobs_mutex);
}

typedef THREAD_MANAGER THREAD_POOL;


void write(pthread_t &threadID ,void *user_data) {
	int* x = (int*)user_data;
	printf("thread id is : %u print(%d)\n", threadID, *x);
}

int a[20];

int main() {

	THREAD_POOL* threadPool = new THREAD_POOL();

	// 10个线程
	int rev = THreadPoolCreate(threadPool, 10);

	if (rev) {
		//创建失败
		exit(0);
	}

	for (int i = 0; i < 20; i++) {
		
		a[i] = i;

		THREAD_POOL_JOB* job = new THREAD_POOL_JOB();
		job->func = &write;
		job->user_data = (void*)(a+i);
		THreadPoolPushJob(threadPool, job);
	}



	return 0;
}

运行截图:

在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
### 回答1: 本章主要针对初学者,简要介绍了c/c++的基本语法和常见的编译错误。首先,介绍了程序的基本结构和常用的数据类型。c语言的基本语句包括赋值语句、条件语句和循环语句等,这些语句都可以组成程序的逻辑结构。接下来,介绍了函数的定义和调用,以及参数传递的方式。对于c++,还介绍了一些面向对象的概念,例如类、对象、成员函数等。同时,也提到了头文件和命名空间的使用方法。 在编程过程中,常常会出现各种编译错误,例如语法错误、类型不匹配、语义错误等,需要学会如何查看和解决这些错误。此外,还介绍了调试工具和各种常用的运算符和表达式,这些都是初学者必须掌握的基础知识。 总的来说,本章是关于c/c++快速入门的一篇简介性文章。虽然只是涉及到了基础的语法和知识点,但对于初学者而言是一个很好的起点。在学习过程中需要不断实践,积累经验,并不断深入了解更高级的编程技术和工具。 ### 回答2: C/C++是一种广泛使用的编程语言,它具有高效、灵活、可移植等特点,在各个领域得到广泛应用。C语言C++语言的基础,在学习C++之前,需要先掌握C语言的基础知识。 本文介绍的是C/C++教程中的第二章——快速入门C/C++。在这一章节中,我们将介绍C语言的基本语法、变量、运算符和流程控制语句等基础知识。以下是C语言的一些基本知识点。 C语言的基本语法: C语言程序由多个函数组成,其中一个函数必须命名为main(),程序从该函数开始执行。C程序中的语句以分号结束,注释使用“//”表示单行注释,“/* */”表示多行注释。 变量和数据类型: C语言中变量的定义格式为“数据类型 变量名”;数据类型包括基本类型和用户自定义类型。C语言中的基本类型有int类型、char类型、float类型和double类型等。其中,int类型表示整型,char类型表示字符型,float类型和double类型表示浮点型。 运算符: C语言中的运算符包括算术运算符、关系运算符、逻辑运算符等。例如,“+”表示加法运算符,“>=”表示大于等于运算符,“&&”表示逻辑与运算符。 流程控制语句: C语言中的流程控制语句包括if语句、switch语句、while语句、do-while语句和for语句等。这些语句可以根据条件执行相应的语句块。 总之,本章节的快速入门C/C++,具有基本语法、变量、运算符和流程控制语句等基础知识。初学者可以通过这些基础知识,轻松入门C/C++,为后续学习打下基础。同时,要注意编写代码的规范和逻辑性,才能更好的理解和使用C/C++语言。 ### 回答3: C语言是一门广泛使用的编程语言,具有高效、灵活、稳定等特点,被广泛应用于嵌入式系统、操作系统、驱动程序、多媒体应用等领域。学习C语言是程序员的必备技能之一。 第二章的快速入门C/C++教程,主要介绍了C/C++语言的基础知识,重点是程序的结构和输入输出。其中程序结构包括函数、语句、变量、表达式等,而输入输出则包括scanf、printf、getchar和putchar等函数。 # 程序结构 程序结构是指程序的基本构成单元,包括函数、语句、变量、表达式等。C语言中的程序结构主要包含以下几个方面。 ## 函数 C语言中,函数是程序的基本组成单元。一个C程序可以由一个或多个函数组成,每个函数可以完成一个任务。函数的格式如下: ```c 返回类型 函数名(参数1, 参数2, ...){ // 函数体 return 返回值; } ``` 其中,返回类型指函数执行后的返回值类型;函数名是由程序员定义的,用于调用函数时识别函数;参数列表是函数的输入参数,可以有多个参数,每个参数由类型和变量名组成;函数体是函数要执行的代码块;return语句可以返回函数的执行结果。 ## 语句 语句是完成特定功能的一组指令。C语言中的语句包括赋值语句、条件语句、循环语句等。C语言中通常使用花括号来表示语句块。例如,下面是一个if语句的例子。 ```c if(条件){ // if语句块 }else{ // else语句块 } ``` 如果条件为真,则执行if语句块中的代码;否则执行else语句块中的代码。 ## 变量 变量是用于存储数据的一种容器。在C语言中,一个变量包括变量名、类型和值。变量名由程序员定义,用于识别变量;类型指变量的数据类型,如整型、字符型、实型等;值是存储在变量中的数据。变量的定义格式如下。 ```c 数据类型 变量名 = 值; ``` 例如,下面是一个整型变量的定义。 ```c int num = 10; ``` ## 表达式 表达式是由变量、运算符和常量组成的一个具有返回值的语句。C语言中的运算符分为算术运算符、关系运算符、逻辑运算符等,例如加号、减号、乘号、除号等。下面是一个简单的表达式。 ```c a = 5 + 6 * 3 / 2 - 1; ``` 这个表达式将计算5加6乘3除以2减1的值,并将结果赋给a变量。 # 输入输出 输入输出是程序中非常重要的部分,可以让程序与用户进行交互。C语言中有多种输入输出函数,其中一些最常用的是scanf、printf、getchar和putchar函数。 ## scanf函数 scanf函数用于从标准输入读取格式化数据,并将读取的数据存储到变量中。它的格式如下。 ```c scanf("格式控制字符串", 变量列表); ``` 其中,格式控制字符串指示scanf函数需要读取的数据类型和格式,变量列表指向要读取的变量。下面是一个scanf函数的例子。 ```c int num; scanf("%d", &num); ``` 这个代码段将从标准输入读取一个整数,并将其存储到num变量中。 ## printf函数 printf函数用于将格式化数据输出到标准输出。它的格式如下。 ```c printf("格式控制字符串", 参数列表); ``` 其中,格式控制字符串指示printf函数需要输出的数据类型和格式,参数列表包含要输出的变量和常量。下面是一个printf函数的例子。 ```c int num = 5; printf("num的值是%d\n", num); ``` 这个代码段将输出“num的值是5”。 ## getchar和putchar函数 getchar函数用于从标准输入读取一个字符,putchar函数用于将一个字符输出到标准输出。它们的用法非常简单,例如下面的代码将读取一个字符并将其转换成大写字母后输出。 ```c char c = getchar(); putchar(toupper(c)); ``` 以上就是第二章中的快速入门C/C++教程的主要内容,包括程序结构和输入输出方面的基础知识。熟练掌握这些内容,对于学习C语言来说是非常重要的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只酷酷光儿( CoolGuang)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值