浅谈线程池

1、为什么要使用线程池?
        在传统服务器结构中,有一个总的监听线程监听有没有新的用户连接服务器,当每有一个新的用户进入时,服务器就会开启一个新的线程用于处理这个用户的数据包。这个线程只服务于这个用户,当用户与服务器关闭连接以后,服务器就会销毁这个线程。然而频繁的开辟与销毁线程极大的占用了系统的资源。如果有大量的用户请求的情况下,系统为了开辟和销毁线程就会浪费大量的时间和资源。既然有问题的出现就会有相应的解决方案,线程池提供了一个外部用户大量与有限资源的矛盾,用开解决当外部有多个请求时,减少线程的创建和销毁所占用的资源,以此来提高系统资源的利用率。当然如果线程创建和销毁的时间相比于任务执行的时间可以忽略不计的话,就没有必要使用线程池了。
2、使用线程池的优势?
     (1)减少在创建和销毁线程上所花的时间以及系统资源的开销 
     (2)如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”。 
     (3)提高系统的相应速度。  
3、线程池的基本原理
        线程池和传统的一个用户对应一个线程的处理方法不同,它的基本思想是在程序开始的时候就在内存中开辟一些线程,现成的数目是固定的,他们独自形成一个类,屏蔽对外的操作,而服务器只需要将数据包交给线程池就可以了。当有新的用户请求到达时,不是新创键一个线程为其服务,而是从所创建的线程中选择一个空闲的线程服务新的客户端,当服务完毕时,线程进入空闲线程池中。如果没有空闲线程的话,就将数据包暂时积累,等待线程池内有空闲的线程以后再进行处理。通过重用一个线程对已存在的任务进行服务,降低了线程对象创建和销毁的开销。当用户请求时,线程已经存在,可以提高请求的响应时间,从而整体的提高了系统的服务效率。
4、 线程池的组成部分
     (1)线程管理器:用于创建和管理线程。
     (2)工作线程:线程池中实际执行任务的线程。在初始化线程时会预先创建好固定数目的线程在池中,这些初始化的线程一般处于空闲状态,一般不占用CPU,占用较小的内存空间。如果一旦任务队列中有任务添加时,会唤醒处于等待状态的线程来执行新添加的任务。
    (3)任务接口:每个任务必须实现的接口(即一个回调函数),当线程池的任务队列中有可执行任务时,被空闲的工作线程调去执行(线程的闲与忙是通过互斥量实现的)把任务抽象出来形成接口,可以做到线程池与具体的任务无关。
    (4)任务队列:用来存放没有处理的任务,提供一种缓冲机制,实现这种结构有好几种方法,常用的是队列,主要运用先进先出原理,另外一种是链表之类的数据结构,可以动态的为它分配内存空间,应用中比较灵活。

下面是自己对线程池理解时画的一个建议的图:


5、一个线程池的例子。

#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pthread.h>

/*线程池中所有运行和等待的任务都是一个CThread_worker
 * 由于所有任务都在链表里,所以是一个链表结构
 */
typedef struct worker
{
	/*回调函数,任务运行时会调用次函数*/
	 void *(*process)(void *arg);     //返回值为指针的函数指针
	 void *arg;                       //回调函数的参数
	 struct worker *next;
} CThread_worker;

/*线程池结构*/
typedef struct 
{
	pthread_mutex_t queue_lock;
	pthread_cond_t  queue_ready;

	/*链表结构,线程池中所有的等待任务*/
	CThread_worker *queue_head;

	/*是否销毁线程池*/
	int shutdown;
	pthread_t *threadid;
	/*线程池中允许的活动线程数目*/
	int max_thread_num;
	/*当前等待队列的任务数目*/
	int cur_queue_size;
} CThread_pool;

int pool_add_worker(void *(process)(void *arg), void *arg);
void *thread_routine(void *arg);

//共享资源
static CThread_pool *pool = NULL;

//初始化线程池
void pool_init(int max_thread_num)
{
	pool = (CThread_pool *)malloc(sizeof(CThread_pool));

	pthread_mutex_init(&(pool->queue_lock), NULL);
	pthread_cond_init(&(pool->queue_ready), NULL);

	pool->queue_head = NULL;

	pool->max_thread_num = max_thread_num;
	pool->cur_queue_size = 0;

	pool->shutdown = 0;

	pool->threadid = (pthread_t *)malloc(max_thread_num * sizeof(pthread_t));
	int i = 0;
	for (i = 0; i < max_thread_num; i++)
	{
		pthread_create(&(pool->threadid[i]), NULL, thread_routine, NULL);
	}
}

void *thread_routine(void *arg)
{
	printf("strating thread %u\n", pthread_self());

	while(1)
	{
		pthread_mutex_lock(&(pool->queue_lock));
		/*如果等待线程为0并且不销毁线程池,则处于阻塞状态;
		 * 注意pthread_cond_wait是一个原子操作,等待前会解锁,
		 * 唤醒后会加锁
		*/
		while(pool->cur_queue_size == 0 && !pool->shutdown)
		{
			printf("thread %u is waiting\n", pthread_self());
			pthread_cond_wait(&(pool->queue_ready), &(pool->queue_lock));
		}

		/*线程池要销毁*/
		if (pool->shutdown)
		{
			/*遇到break, continue, return等跳转语句,千万不要忘记先解锁*/
			pthread_mutex_unlock(&(pool->queue_lock));
			printf("thread %u will exit.\n", pthread_self());
			pthread_exit(NULL);
		}

		printf("thread %u is starting to work\n", pthread_self());

		//assert用于调试
		assert(pool->cur_queue_size != 0);
		assert(pool->queue_head != NULL);

		/*等待队列长度减去1,并取出链表中的头元素*/
		pool->cur_queue_size--;
		CThread_worker *worker = pool->queue_head;
		pool->queue_head = worker->next;
		pthread_mutex_unlock(&(pool->queue_lock));

		/*调用回调函数,执行任务*/
		(*(worker->process))(worker->arg);
		free(worker);
		worker = NULL;
	}

	pthread_exit(NULL);
}

/*向线程池中添加任务*/
int pool_add_worker(void *(*process)(void *arg), void *arg)
{
	printf("add worker is %d\n", *(int *)arg);
	/*构造一个新任务*/
	CThread_worker *newworking = (CThread_worker *)malloc(sizeof(CThread_worker));
	newworking->process = process;
	newworking->arg = arg;
	newworking->next = NULL;

	pthread_mutex_lock(&(pool->queue_lock));
	/*将任务加到等待队列中*/
	CThread_worker *member = pool->queue_head;
	if (member != NULL)
	{
		while (member->next != NULL)
		{
			member = member->next;
		}
		member->next = newworking;
	}
	else
	{
		pool->queue_head = newworking;
	}

	assert(pool->queue_head != NULL);

	pool->cur_queue_size++;
	pthread_mutex_unlock(&(pool->queue_lock));

	/*等待队列中有任务了,唤醒一个等待线程,
	 * 注意所有的线程都在忙碌,这句话就不会起作用
	*/
	pthread_cond_signal(&(pool->queue_ready));
	return 0;
}

void *myprocess(void *arg)
{
	printf("thread is %u, working on task %d\n", pthread_self(), *(int *)arg);
	sleep(1);        /*休息1秒,延长任务执行时间*/
	return NULL;
}

/*销毁线程池,等待队列中的任务不会再执行,但是正在运行的线程会一直把任务运行完后退出*/
int pool_destroy()
{
	if (pool->shutdown)
	      return -1;         /*防止2次调用*/
	pool->shutdown = 1;

	/*唤醒所有等待的线程,线程池要销毁*/
	pthread_cond_broadcast(&(pool->queue_ready));

	/*阻塞等待线程退出,否则就会编程僵尸进程*/
	int i;
	for (i = 0; i < pool->max_thread_num; i++)
	      pthread_join(pool->threadid[i], NULL);
	free(pool->threadid);

	/*销毁等待队列*/
	CThread_worker *head = NULL;
	while (pool->queue_head != NULL)
	{
		head = pool->queue_head;
		pool->queue_head = pool->queue_head->next;
		free(head);
	}

	/*条件变量和互斥量也要销毁*/
	pthread_mutex_destroy(&(pool->queue_lock));
	pthread_cond_destroy(&(pool->queue_ready));

	free(pool);
	/*销毁指针置空*/
	pool = NULL;
	printf("thread pool destroy finish!\n");
	return 0;
}

int main(int argc, char *argv[])
{
	pool_init(3);               /*线程池中最多有3个线程*/

	/*连续向池中投放10个任务*/
 	int *workingnum = (int *)malloc(sizeof(int) * 10);
	int i;
	for (i = 0; i < 10; i++)
	{
		workingnum[i] = i;
		pool_add_worker(myprocess, &workingnum[i]);
	}

	/*等待所有的任务完成*/
	sleep(5);

	/*销毁线程池*/
	pool_destroy();

	free(workingnum);
	return 0;
}
运行结果:
add worker is 0
add worker is 1
add worker is 2
add worker is 3
add worker is 4
add worker is 5
add worker is 6
add worker is 7
add worker is 8
add worker is 9
strating thread 3067124544
thread 3067124544 is starting to work
thread is 3067124544, working on task 0
strating thread 3075517248
thread 3075517248 is starting to work
thread is 3075517248, working on task 1
strating thread 3058731840
thread 3058731840 is starting to work
thread is 3058731840, working on task 2
thread 3075517248 is starting to work
thread is 3075517248, working on task 3
thread 3067124544 is starting to work
thread is 3067124544, working on task 4
thread 3058731840 is starting to work
thread is 3058731840, working on task 5
thread 3075517248 is starting to work
thread is 3075517248, working on task 6
thread 3067124544 is starting to work
thread is 3067124544, working on task 7
thread 3058731840 is starting to work
thread is 3058731840, working on task 8
thread 3075517248 is starting to work
thread is 3075517248, working on task 9
thread 3067124544 is waiting
thread 3058731840 is waiting
thread 3075517248 is waiting
thread 3067124544 will exit.
thread 3058731840 will exit.
thread 3075517248 will exit.
thread pool destroy finish!


### Spring Boot框架简介与使用方法 Spring Boot 是由 Pivotal 团队提供的一个开源框架,旨在简化基于 Spring 框架的项目开发过程。它遵循“约定大于配置”的设计原则,减少了开发者在配置上的工作量[^2]。通过内置的默认配置和自动化配置机制,Spring Boot 提供了快速构建独立、生产级别的 Spring 应用程序的能力。 #### 1. Spring Boot 的核心概念 Spring Boot 的核心理念是减少开发者的负担,提供开箱即用的功能。以下是 Spring Boot 的几个关键特性: - **自动化配置**:Spring Boot 根据类路径中的依赖项自动配置 Spring 应用程序。例如,当引入数据库驱动依赖时,Spring Boot 会自动配置数据源和相关的 Bean 对象[^2]。 - **嵌入式服务器**:Spring Boot 内置了 Tomcat、Jetty 或 Undertow 等 Web 容器,使得应用程序可以独立运行而无需外部部署环境。 - **起步依赖(Starters)**:Spring Boot 提供了一系列的 Starter 模块,这些模块封装了常用的依赖和配置,方便开发者快速集成各种功能。例如,`spring-boot-starter-web` 提供了构建 Web 应用所需的所有依赖[^2]。 - **监控支持**:通过引入 `spring-boot-starter-actuator` 依赖,开发者可以获得丰富的监控指标,包括应用健康状态、线程池信息、内存使用情况等[^1]。 #### 2. Spring Boot 的执行流程 Spring Boot 的主要执行流程可以分为以下几个阶段: - **加载配置**:Spring Boot 从多个来源(如 `application.properties` 或 `application.yml` 文件)加载配置属性,并将其注入到应用程序中。 - **自动化配置**:根据类路径中的依赖项和配置属性,Spring Boot 自动配置 Spring 上下文。 - **启动嵌入式容器**:如果应用程序是一个 Web 应用,Spring Boot 将启动嵌入式 Web 容器。 - **运行应用程序**:完成上述步骤后,Spring Boot 启动应用程序并开始处理请求[^1]。 #### 3. 创建一个简单的 Spring Boot 项目 以下是创建和运行一个简单 Spring Boot 项目的步骤: 1. **添加依赖** 在 `pom.xml` 文件中添加 Spring Boot 的依赖项: ```xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.0</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> ``` 2. **编写主类** 创建一个包含 `@SpringBootApplication` 注解的 Java 类: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ``` 3. **创建控制器** 添加一个 REST 控制器来处理 HTTP 请求: ```java import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String sayHello() { return "Hello, Spring Boot!"; } } ``` 4. **运行项目** 使用以下命令运行项目: ```bash mvn spring-boot:run ``` 访问 `http://localhost:8080/hello` 即可看到输出结果。 #### 4. Spring Boot 的优势 Spring Boot 提供了诸多优点,使其成为现代 Java 开发的首选框架之一: - **快速集成**:通过 Starter 模块,开发者可以秒级集成各种框架[^2]。 - **减少配置**:完全抛弃繁琐的 XML 配置,采用注解和配置文件的方式进行开发[^2]。 - **内置监控**:通过 Actuator 模块,开发者可以获取丰富的监控指标[^1]。 - **易于扩展**:虽然 Spring Boot 本身是一个微框架,但它可以通过 Spring Cloud 等工具扩展以支持服务发现、注册、负载均衡等功能[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值