精华内容
下载资源
问答
  • 线程池和进程池

    2018-12-02 15:38:56
    动态创建子进程(函数线程)实现并发服务器的缺点 在前面的文章中我们是通过动态创建子进程(函数线程)来实现并发服务器的,这样做的缺点如下: 动态创建进程(或线程)是比较耗费时间的,这样导致较慢的客户响应...

    动态创建子进程(函数线程)实现并发服务器的缺点

    在前面的文章中我们是通过动态创建子进程(函数线程)来实现并发服务器的,这样做的缺点如下:

    1. 动态创建进程(或线程)是比较耗费时间的,这样导致较慢的客户响应
    2. 动态创建的子进程(子线程)通常只用来为一个客户服务,这将导致系统上产生大量的细微进程(或线程)。进程间的切换将消耗大量的CPU时间
    3. 动态创建的子进程是当前进程的完整映像,当前进程必须谨慎地管理其分配的文件描述符和堆内存等系统资源,否则子进程可能复制这些资源,从而使系统的可用资源急剧下降,进而影响服务器的性能

    为了解决这些问题,我们使用了进程池和线程池这两种技术。

    进程池和线程池概述

    • 设计思想:进程池是由服务器预先创建的一组子进程,在服务器程序启动时就创建,当有客户端连接时,就从池中分配进程或者线程为客户端进行服务。这些子进程的典型数目在3~10个之间。httpd守护进程就是使用包含8个子进程的进程池来实现并发的。线程池中的线程数量应该和CPU数量差不多

    httpd的进程池:

    我们可以清楚地看到,当我们开启httpd服务后,查看进程时,有8个父进程都是9863的子进程,这就是httpd创建的进程池。

    • 优点:
    1. 进程池中的所有子进程都运行着相同的代码,并具有相同的属性,比如优先级,PGID等。因为进程池在服务器启动之初就创建好了,所以每个子进程都相对干净,即他们没有打开不必要的文件描述符(从父进程继承而来),也不会错误地使用大块的堆内存。
    2. 当有新的任务到来时,主进程通过某种进程间通讯的方式选择进程池中的某个子进程来为之服务。相比于动态创建,选择一个已有的子进程(函数线程)的代价显然要小得多

    线程池的实现难点

    1. 主线程需要将文件描述符,传递给函数线程
    2. 函数线程启动后必须阻塞在获取文件描述符之前
    3. 信号量来控制主线程向函数线程通知获取文件描述符事件
    4. 主线程在数组中插入数据,以及函数线程获取数组中的数据必须是一个互斥的过程

    进程池的实现难点

    1. 父进程需要将文件描述符传递给子进程
    2. 由于文件描述符是在fork之后创建,因此父子进程无法共享
    3. 进程池中的进程必须阻塞在获取到客户端的文件描述符之前
    4. 传递文件描述符时,不能仅仅传递一个整型值,而是文件描述符

    进程池源码

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<string.h>
    #include<assert.h>
    #include<fcntl.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<netinet/in.h>
    
    static const int CONTROL_LEN = CMSG_LEN(sizeof(int));
    
    /*文件描述符发送函数*/
    void send_fd(int fd, int fd_to_send)
    {
    	struct iovec iov[1];
    	struct msghdr msg;
    	char buf[0];
    
    	iov[0].iov_base = buf;
    	iov[0].iov_len = 1;
    	msg.msg_name = NULL;
    	msg.msg_namelen = 0;
    	msg.msg_iov = iov;
    	msg.msg_iovlen = 1;
    
    	struct cmsghdr cm;
    	cm.cmsg_len = CONTROL_LEN;
    	cm.cmsg_level = SOL_SOCKET;
    	cm.cmsg_type = SCM_RIGHTS;
    	*(int *)CMSG_DATA(&cm) = fd_to_send;
    	msg.msg_control = &cm;
    	msg.msg_controllen = CONTROL_LEN;
    
    	sendmsg(fd, &msg, 0);
    }
    
    
    /*文件描述符接收函数*/
    int recv_fd(int fd)
    {
    	struct iovec iov[1];
    	struct msghdr msg;
    	char buf[0];
    
    	iov[0].iov_base = buf;
    	iov[0].iov_len = 1;
    	msg.msg_name = NULL;
    	msg.msg_namelen = 0;
    	msg.msg_iov = iov;
    	msg.msg_iovlen = 1;
    
    	struct cmsghdr cm;
    	msg.msg_control = &cm;
    	msg.msg_controllen = CONTROL_LEN;
    
    	recvmsg(fd, &msg, 0);
    
    	int fd_to_read = *(int*)CMSG_DATA(&cm);
    
    	return fd_to_read;
    }
    
    int main()
    {
    	int pipefd[2];
    	int res = socketpair(PF_UNIX, SOCK_DGRAM, 0, pipefd);//创建管道为发送文件描述符做准备
    	int sockfd = socket(PF_INET, SOCK_STREAM, 0);
    	assert(sockfd != -1);
    
    	struct sockaddr_in ser, cli;
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(6500);
    	ser.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    	res = bind(sockfd, (struct sockaddr*)&ser, sizeof(ser));
    	assert(res != -1);
    
    	listen(sockfd, 5);
    	
    	int ret;
    	int i = 0;
    /*接收连接前先创建进程池*/
    	for(; i < 3; i++)
    	{
    		ret = fork();
    		if(ret == 0)
    		{
    			break;
    		}
    	}
    	
    	if(ret != 0)
    	{
    		close(pipefd[0]);//父进程关闭读通道
    		while(1)
    		{
    			socklen_t len = sizeof(cli);
    			int c = accept(sockfd, (struct sockaddr*)&cli, &len);
    			if(c == -1)
    			{
    				printf("accept error\n");
    				continue;
    			}
    			send_fd(pipefd[1], c);//将接收到的文件描述符写入管道,供子进程读取
    			close(c);
    		}
    	}
    	if(ret == 0)
    	{
    		while(1)
    		{
    			close(pipefd[1]);//子进程关闭写通道
    			int c = recv_fd(pipefd[0]);//读取管道中的文件描述符
    			while(1)
    			{
    				char recvbuff[128] = {0};
    				res = recv(c, recvbuff, 127, 0);
    				if(res <= 0)
    				{
    					printf("%ddisclient\n", c);
    					close(c);
    					break;
    				}
    				printf("%d: %s\n", c, recvbuff);
    				send(c, "OK", 2, 0);
    			}
    		}
    	}
    }

    线程池源码

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<string.h>
    #include<assert.h>
    #include<pthread.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<netinet/in.h>
    #include<semaphore.h>
    
    pthread_mutex_t mutex;
    int clilink[8];//已经被接收但还未被处理的连接请求
    sem_t sem;
    /*初始化连接数组*/
    void InitCliLink()
    {
    	int i = 0;
    	for(; i < 8; i++)
    	{
    		clilink[i] = -1;
    	}
    }
    
    /*插入新接收的连接,等待处理*/
    int Insert(int c)
    {
    	pthread_mutex_lock(&mutex);//插入的同时时不能获取,用锁控制
    	int i = 0;
    	for(; i < 8; i++)
    	{
    		if(clilink[i] == -1)
    		{
    			clilink[i] = c;
    			break;
    		}
    	}
    	pthread_mutex_unlock(&mutex);
    	if(i >= 8)
    		return -1;
    	return 0;
    }
    
    /*获取连接,进行处理*/
    int GetCli()
    {
    	pthread_mutex_lock(&mutex);//获取时不能插入
    	int i = 0;
    	int c = clilink[0];
    	for(; i < 7; i++)
    	{
    		clilink[i] = clilink[i+1];
    	}
    	pthread_mutex_unlock(&mutex);
    	return c;
    }
    
    void *pthread_fun(void *arg);
    
    int main()
    {
    	int sockfd = socket(PF_INET, SOCK_STREAM, 0);
    	assert(sockfd != -1);
    
    	struct sockaddr_in ser, cli;
    	memset(&ser, 0, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(6500);
    	ser.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    	int res = bind(sockfd, (struct sockaddr*)&ser, sizeof(ser));
    	assert(res != -1);
    
    	listen(sockfd, 5);
    
    
    /*创建线程池*/
    	int i = 0;
    	for(; i < 3; i++)
    	{
    		pthread_t id;
    		res = pthread_create(&id, NULL, pthread_fun, NULL);
    	}
    	
    	InitCliLink();
    	sem_init(&sem, 0, 0);//初始化信号量值为0,使得函数线程阻塞
    
    	while(1)
    	{
    		int len = sizeof(cli);
    		int c = accept(sockfd, (struct sockaddr*)&cli, &len);
    		if(c < 0)
    		{
    			continue;
    		}
    		
    		if(Insert(c) == -1)
    		{
    			close(c);
    			continue;
    		}
    		sem_post(&sem);//信号量+1,函数线程可以开始获取数组中等待的连接进行处理
    	}
    }
    
    /*线程函数*/
    void *pthread_fun(void *arg)
    {
    	while(1)
    	{
    		sem_wait(&sem);
    		int c = GetCli();
    			while(1)
    			{
    				char buff[128] = {0};
    				int n = recv(c, buff, 127, 0);
    				if(n <= 0)
    				{
    					close(c);
    					break;
    				}
    				printf("%d: %s\n",c, buff);
    				send(c, "ok", 2, 0);
    			}
    	}
    }

     

    展开全文
  • 线程池代替进程池

    千次阅读 2015-03-27 11:08:19
    底层库的任务调度用的是进程池的实现,也就是创建许多子进程来进行执行业务处理,进程池技术是比较早的一种技术了,在现在众多高性能服务端程序中,包括开源著名的apache,nginx…,大多采用线程池来实现,下面说一下...

      

    一.问题引入

    刚进入一家新公司,在公司的一些培训中得知,我们的服务端系统中,底层库的任务调度用的是进程池的实现,也就是创建许多子进程来进行执行业务处理,进程池技术是比较早的一种技术了,在现在众多高性能服务端程序中,包括开源著名的apache,nginx…,大多采用线程池来实现,下面说一下采用线程的一些好处。

     

    二.线程的优点

    线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者。传统的Unix也支持线程的概念,但是在一个进程(process)中只允许有一个线程,这样多线程就意味着多进程。现在,多线程技术已经被许多操作系统所支持,包括Windows/NT,当然,也包括Linux

      为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?我们首先必须回答这些问题。

      使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计,总的说来,一个进程的开销大约是一个线程开销的30倍左右,当然,在具体的系统上,这个数据可能会有较大的区别。

      使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。

      除了以上所说的优点外,不和进程比较,多线程程序作为一种多任务、并发的工作方式,当然有以下的优点:

      1) 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。

      2) 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同的线程运行于不同的CPU上。

    3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。

      

    三.线程池原理

    传统服务端程序利用线程技术响应客户请求,每来一个客户请求,服务端就会启动一个线程处理请求,结果后线程自动退出,这样做似乎效率提高了,但是如果请求很频繁,就会导致服务端频繁地创建线程,销毁线程,比较消耗CPU资源,所以才会需要进一步地改进,这样线程池技术就出现了。

    线程池的原理:系统预先启动一定数量的线程,构成一个线程池,并利用手段将它们处于阻塞状态,在阻塞状态下的线程是不消耗CPU资源的,每一个客户请求视为一个任务,将这些任务都放入一个队列中,并通知线程池中的线程取出任务队列中的任务请求进行处理。可将任务请求看作是生产者,将处理任务的线程池看作是消费者,这就是生产者消费者模型:

        这会涉及到任务队列在多线程中的同步问题,以后再细节讨论如何处理同步。 

     

    四.高并发高性能服务端设计

    设计高性能服务端程序,线程池只是其中提升服务器性能的一种方式,其它还有:网络IP模型Epoll,IOCP,内存池(开源库nedmalloc)、数据库缓存系统(开源库memcache)、数据库连接池。。。许多提升性能的实现都已经有开源代码可用了。

    另外还有通信协议上的选择也会影响服务端性能,对于大批量小数据多客户端并发,可以考虑用UDP协议,由于UDP是一种无连接传输协议,可以有效的降低系统开销,特别对大批量小数据的请求,效果明显,而TCP在这种情况对系统的消耗是很大的,往往会导致连接被拒绝,或者socket句柄已经存在的错误。有人可能就会问了,在服务端可以采用短连接,这样就可以节约资源。其实不然,大量socket的创建与销毁本来就是一种对系统性能的极度消耗,有时这种还没有直接使用长连接来的效率高。

    本人个人兴趣方面是高并发高性能服务端设计,经验还不算丰富,一直在积累中,有兴趣的同学可以向我提建议,也可以找我讨论。

    本人联系方式:

    QQ:176013544

     

     

     

     

    展开全文
  • 由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间,即“浪费”服务器的硬件资源,以换取其运行效率。这就是的概念。 是一组资源的集合,这组资源在服务器启动之初就被...

    由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间,即“浪费”服务器的硬件资源,以换取其运行效率。这就是池的概念。

    池是一组资源的集合,这组资源在服务器启动之初就被创建并初始化,这称为静态资源分配。

    当服务器进入正式运行阶段,即开始处理客户请求的时候,如果它需要相关的资源,就可以直接从池中获取,无需动态分配。很显然,直接从池中取得所需资源比动态分配资源的速度要快得多,因为分配系统资源的系统调用都是很耗时的。

    当服务器处理完一个客户连接后,可以把相关的资源放回池中,无需执行系统调用来释放资源。从最终效果来看,池相当于服务器管理系统资源的应用设施,它避免了服务器对内核的频繁访问。提高了效率。

    池可以分为很多种,常见的有进程池,线城池,内存池。


    内存池

    内存池是一种内存分配方式。通常我们直接使用new、malloc等系统调用申请分配内存,这样做的缺点在于:由于所申请内存块的大小不定,当频繁使用时会造成大量的内存碎片并进而降低性能。

    内存池则是在真正使用内存之前,先申请分配一定数量的、大小相等的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是,使得内存分配效率得到提升。


    进程池&&线程池

    在面向对象程序编程中,对象的创建与析构都是一个较为复杂的过程,较费时间,所以为了提高程序的运行效率尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁。
    所以我们可以创建一个进程池(线程池),预先放一些进程(线程)进去,要用的时候就直接调用,用完之后再把进程归还给进程池,省下创建删除进程的时间,不过当然就需要额外的开销了。
    利用线程池与进程池可以使管理进程与线程的工作交给系统管理,不需要程序员对里面的线程、进程进行管理。

    以进程池为例

    进程池是由服务器预先创建的一组子进程,这些子进程的数目在 3~10 个之间(当然这只是典型情况)。线程池中的线程数量应该和CPU数量差不多。

    进程池中的所有子进程都运行着相同的代码,并具有相同的属性,比如优先级、 PGID 等。

    当有新的任务来到时,主进程将通过某种方式选择进程池中的某一个子进程来为之服务。相比于动态创建子进程,选择一个已经存在的子进程的代价显得小得多。至于主进程选择哪个子进程来为新任务服务,则有两种方法:

    • 主进程使用某种算法来主动选择子进程。最简单、最常用的算法是随机算法和Round Robin(轮流算法)。
    • 主进程和所有子进程通过一个共享的工作队列来同步,子进程都睡眠在该工作队列上。当有新的任务到来时,主进程将任务添加到工作队列中。这将唤醒正在等待任务的子进程,不过只有一个子进程将获得新任务的“接管权”,它可以从工作队列中取出任务并执行之,而其他子进程将继续睡眠在工作队列上。

    当选择好子进程后,主进程还需要使用某种通知机制来告诉目标子进程有新任务需要处理,并传递必要的数据。最简单的方式是,在父进程和子进程之间预先建立好一条管道,然后通过管道来实现所有的进程间通信。在父线程和子线程之间传递数据就要简单得多,因为我们可以把这些数据定义为全局,那么它们本身就是被所有线程共享的。


    线程池的应用

    线程池主要用于
    1、需要大量的线程来完成任务,且完成任务的时间比较短。
    WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。
    因为单个任务小,而任务数量巨大,一个热门网站的点击次数会很多。
    但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。

    2、对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。

    3、接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。


    线程池&&进程池的好处

    进程池进程池减少了创建,归还的时间。提高了效率。


    用C++模拟线程池

    Linux系统下用C语言创建的一个线程池。线程池会维护一个任务链表(每个CThread_worker结构就是一个任务)。
    pool_init()函数预先创建好max_thread_num个线程,每个线程执thread_routine ()函数。该函数中

    while (pool->cur_queue_size == 0)
    {
          pthread_cond_wait (&(pool->queue_ready),&(pool->queue_lock));
    }
       
    • 1
    • 2
    • 3
    • 4

    表示如果任务链表中没有任务,则该线程出于阻塞等待状态。否则从队列中取出任务并执行。
    pool_add_worker()函数向线程池的任务链表中加入一个任务,加入后通过调用pthread_cond_signal (&(pool->queue_ready))唤醒一个出于阻塞状态的线程(如果有的话)。
    pool_destroy ()函数用于销毁线程池,线程池任务链表中的任务不会再被执行,但是正在运行的线程会一直把任务运行完后再退出。

    具体代码:

    #include <stdio.h>  
    #include <stdlib.h>  
    #include <unistd.h>  
    #include <sys/types.h>  
    #include <pthread.h>  
    #include <assert.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;

    <span class="hljs-comment">/*链表结构,线程池中所有等待任务*/</span>  
    CThread_worker *queue_head;  
    
    <span class="hljs-comment">/*是否销毁线程池*/</span>  
    <span class="hljs-keyword">int</span> shutdown;  
    pthread_t *threadid;  
    <span class="hljs-comment">/*线程池中允许的活动线程数目*/</span>  
    <span class="hljs-keyword">int</span> max_thread_num;  
    <span class="hljs-comment">/*当前等待队列的任务数目*/</span>  
    <span class="hljs-keyword">int</span> 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 (&amp;(pool-&gt;queue_lock), NULL);  
    pthread_cond_init (&amp;(pool-&gt;queue_ready), NULL);  
    
    pool-&gt;queue_head = NULL;  
    
    pool-&gt;max_thread_num = max_thread_num;  
    pool-&gt;cur_queue_size = <span class="hljs-number">0</span>;  
    
    pool-&gt;shutdown = <span class="hljs-number">0</span>;  
    
    pool-&gt;threadid = (pthread_t *) <span class="hljs-built_in">malloc</span> (max_thread_num * <span class="hljs-keyword">sizeof</span> (pthread_t));  
    <span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>;  
    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; max_thread_num; i++)  
    {   
        pthread_create (&amp;(pool-&gt;threadid[i]), NULL, thread_routine,NULL);  
    }  
    

    }

    /向线程池中加入任务/
    int pool_add_worker (void *(*process) (void *arg), void *arg)
    {
    /构造一个新任务/
    CThread_worker *newworker = (CThread_worker *) malloc (sizeof (CThread_worker));
    newworker->process = process;
    newworker->arg = arg;
    newworker->next = NULL;/别忘置空/

    pthread_mutex_lock (&amp;(pool-&gt;queue_lock));  
    <span class="hljs-comment">/*将任务加入到等待队列中*/</span>  
    CThread_worker *member = pool-&gt;queue_head;  
    <span class="hljs-keyword">if</span> (member != NULL)  
    {  
        <span class="hljs-keyword">while</span> (member-&gt;next != NULL)  
            member = member-&gt;next;  
        member-&gt;next = newworker;  
    }  
    <span class="hljs-keyword">else</span>  
    {  
        pool-&gt;queue_head = newworker;  
    }  
    
    assert (pool-&gt;queue_head != NULL);  
    
    pool-&gt;cur_queue_size++;  
    pthread_mutex_unlock (&amp;(pool-&gt;queue_lock));  
    <span class="hljs-comment">/*好了,等待队列中有任务了,唤醒一个等待线程; 
    注意如果所有线程都在忙碌,这句没有任何作用*/</span>  
    pthread_cond_signal (&amp;(pool-&gt;queue_ready));  
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;  
    

    }

    /销毁线程池,等待队列中的任务不会再被执行,但是正在运行的线程会一直
    把任务运行完后再退出
    /

    int pool_destroy ()
    {
    if (pool->shutdown)
    return -1;/防止两次调用/
    pool->shutdown = 1;

    <span class="hljs-comment">/*唤醒所有等待线程,线程池要销毁了*/</span>  
    pthread_cond_broadcast (&amp;(pool-&gt;queue_ready));  
    
    <span class="hljs-comment">/*阻塞等待线程退出,否则就成僵尸了*/</span>  
    <span class="hljs-keyword">int</span> i;  
    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; pool-&gt;max_thread_num; i++)  
        pthread_join (pool-&gt;threadid[i], NULL);  
    <span class="hljs-built_in">free</span> (pool-&gt;threadid);  
    
    <span class="hljs-comment">/*销毁等待队列*/</span>  
    CThread_worker *head = NULL;  
    <span class="hljs-keyword">while</span> (pool-&gt;queue_head != NULL)  
    {  
        head = pool-&gt;queue_head;  
        pool-&gt;queue_head = pool-&gt;queue_head-&gt;next;  
        <span class="hljs-built_in">free</span> (head);  
    }  
    <span class="hljs-comment">/*条件变量和互斥量也别忘了销毁*/</span>  
    pthread_mutex_destroy(&amp;(pool-&gt;queue_lock));  
    pthread_cond_destroy(&amp;(pool-&gt;queue_ready));  
    
    <span class="hljs-built_in">free</span> (pool);  
    <span class="hljs-comment">/*销毁后指针置空是个好习惯*/</span>  
    pool=NULL;  
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;  
    

    }

    void * thread_routine (void *arg)
    {
    printf (“starting thread 0x%x\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 0x%x is waiting\n”, pthread_self ());
    pthread_cond_wait (&(pool->queue_ready), &(pool->queue_lock));
    }

        <span class="hljs-comment">/*线程池要销毁了*/</span>  
        <span class="hljs-keyword">if</span> (pool-&gt;shutdown)  
        {  
            <span class="hljs-comment">/*遇到break,continue,return等跳转语句,千万不要忘记先解锁*/</span>  
            pthread_mutex_unlock (&amp;(pool-&gt;queue_lock));  
            <span class="hljs-built_in">printf</span> (<span class="hljs-string">"thread 0x%x will exit\n"</span>, pthread_self ());  
            pthread_exit (NULL);  
        }  
    
        <span class="hljs-built_in">printf</span> (<span class="hljs-string">"thread 0x%x is starting to work\n"</span>, pthread_self ());  
    
        <span class="hljs-comment">/*assert是调试的好帮手*/</span>  
        assert (pool-&gt;cur_queue_size != <span class="hljs-number">0</span>);  
        assert (pool-&gt;queue_head != NULL);  
    
        <span class="hljs-comment">/*等待队列长度减去1,并取出链表中的头元素*/</span>  
        pool-&gt;cur_queue_size--;  
        CThread_worker *worker = pool-&gt;queue_head;  
        pool-&gt;queue_head = worker-&gt;next;  
        pthread_mutex_unlock (&amp;(pool-&gt;queue_lock));  
    
        <span class="hljs-comment">/*调用回调函数,执行任务*/</span>  
        (*(worker-&gt;process)) (worker-&gt;arg);  
        <span class="hljs-built_in">free</span> (worker);  
        worker = NULL;  
    }  
    <span class="hljs-comment">/*这一句应该是不可达的*/</span>  
    pthread_exit (NULL);  
    

    }

    // 下面是测试代码

    void * myprocess (void arg)
    {
    printf (“threadid is 0x%x, working on task %d\n”, pthread_self (),
    (int *) arg);
    sleep (1);/休息一秒,延长任务的执行时间/
    return NULL;
    }

    int main (int argc, char **argv)
    {
    pool_init (3);/线程池中最多三个活动线程/

    <span class="hljs-comment">/*连续向池中投入10个任务*/</span>  
    <span class="hljs-keyword">int</span> *workingnum = (<span class="hljs-keyword">int</span> *) <span class="hljs-built_in">malloc</span> (<span class="hljs-keyword">sizeof</span> (<span class="hljs-keyword">int</span>) * <span class="hljs-number">10</span>);  
    <span class="hljs-keyword">int</span> i;  
    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++)  
    {  
        workingnum[i] = i;  
        pool_add_worker (myprocess, &amp;workingnum[i]);  
    }  
    <span class="hljs-comment">/*等待所有任务完成*/</span>  
    sleep (<span class="hljs-number">5</span>);  
    <span class="hljs-comment">/*销毁线程池*/</span>  
    pool_destroy ();  
    
    <span class="hljs-built_in">free</span> (workingnum);  
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;  
    

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213

    结果:
    当是个线程被申请完 就会等待所有任务完成 摧毁线城池
    这里写图片描述
    这里写图片描述

    展开全文
  • 池化技术应用广泛,如内存线程池,连接等等。内存相关的内容,建议看看Apache、Nginx等开源web服务器的内存实现。 由于在实际应用当做,分配内存、创建进程、线程都会设计到一些系统调用,系统调用需要...
     
           
    首先介绍一个概念“池化技术 ”。池化技术就是:提前保存大量的资源,以备不时之需以及重复使用。池化技术应用广泛,如内存池,线程池,连接池等等。内存池相关的内容,建议看看ApacheNginx等开源web服务器的内存池实现。

           由于在实际应用当做,分配内存、创建进程、线程都会设计到一些系统调用,系统调用需要导致程序从用户态切换到内核态,是非常耗时的操作。因此,当程序中需要频繁的进行内存申请释放,进程、线程创建销毁等操作时,通常会使用内存池、进程池、线程池技术来提升程序的性能。


           线程池:线程池的原理很简单,类似于操作系统中的缓冲区的概念,它的流程如下:先启动若干数量的线程,并让这些线程都处于睡眠状态,当需要一个开辟一个线程去做具体的工作时,就会唤醒线程池中的某一个睡眠线程,让它去做具体工作,当工作完成后,线程又处于睡眠状态,而不是将线程销毁。


      进程池与线程池同理。


      内存池:内存池是指程序预先从操作系统申请一块足够大内存,此后,当程序中需要申请内存的时候,不是直接向操作系统申请,而是直接从内存池中获取;同理,当程序释放内存的时候,并不真正将内存返回给操作系统,而是返回内存池。当程序退出(或者特定时间)时,内存池才将之前申请的内存真正释放。
    展开全文
  • 的概念 ...由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间...是一组资源的集合,这组资源在服务器启动之初就完全被创建并初始化,这称为静态资源分配。当服务器进入正
  • 7 内存池、线程池进程池及实现 池 由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间,即**“浪费”服务器的硬件资源,以换取其运行效率**。这就是池的概念。 池是一组资源的...
  • python标准库提供线程多处理模块来编写相应的多线程/多进程代码,但当项目达到一定规模时,频繁地创建/销毁进程或线程是非常消耗资源的,此时我们必须编写自己的线程池/进程池来交换时间空间。但是从Python3.2开始...
  • Linux--线程池进程池线程池的简单实现 https://blog.csdn.net/sayhello_world/article/details/72829329 http://blog.chinaunix.net/uid-26430381-id-3746859.html https://www.cnblogs.com/huxiao-tee/   ...
  • 1、的概念  一般来说,服务器的硬件资源相对充裕,很多时候我们使用以空间换时间的方法来提高服务器的性能,不惜浪费更多的空间以换取服务器运行效率。具体做法是提前保存大量的资源,以备不时之需以及重复使用...
  • 进程池线程池

    2017-06-03 17:17:12
    的概念 由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间,即“浪费”服务器的硬件资源,以换取其运行效率。这就是的概念。是一组资源的集合,这组资源在服务器启动之...
  • 进程池线程池,内存池
  • 的概念由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间,即“浪费”服务器的硬件资源,以换取其运行效率。这就是的概念。是一组资源的集合,这组资源在服务器启动之初就...
  • 内存池 平常我们使用new、malloc在堆区申请一块内存,但由于每次申请的内存大小不一样就会产生很多内存...进程池&&线程池 这两个问题有一定的相似度,在面向对象程序编程中,对象的创建与析构都是一个较为...
  • day33-进程池和线程池

    2019-09-21 03:36:42
    进程池线程池 开进程开线程都需要消耗资源,只不过两者比较的情况线程消耗的资源比较少 在计算机能够承受范围之内最大限度的利用计算机 什么是池? # 在保证计算机硬件安全的情况下最大限度的利用计算机 池...
  • 1.进程池和线程池 2.异步回调 3.协程 4.基于TCP使用多线程实现高并发 一.进程池和线程池 什么是进程池和线程池: ''' 池 Pool 指的是一个容器 线程池就是用来存储线程对象的 容器 创建池子 可以...
  • 进程池线程池

    2017-11-14 18:12:20
    的概念 由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间,即“浪费”服务器的硬件资源,以换取其运行效率。这就是的概念。是一组资源的集合,这组资源在服务器启动之初...
  • 进程池线程池

    2018-07-12 09:18:09
    进程池线程池池的概念由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间,即“浪费”服务器的硬件资源,以换取其运行效率。这就是池的概念。池是一组资源的集合,这组资源在...
  • 内存池、进程池线程池池的概念内存池进程池和线程池线程池主要应用来源 池的概念   由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间,即“浪费”服务器的硬件资源,以换取...
  • Python队列 线程池 进程池 基本概念以及使用方法 线程、进程概念 算是对上一篇文章的回顾,具体细节参见上文。 概念 进程:一个正在运行的应用程序就是一个进程。一个进程是运行在其专用且受保护的内存空间中 线程:...
  • 的概念 由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间,即“浪费”服务器的硬件资源,以换取其运行效率。这就是的概念。是一组资源的集合,这组资源在服务器启动之初...
  • 进程池线程池 提交任务的方式: 同步:提交任务之后,原地等待任务的返回结果,期间不做任何事 异步:提交任务之后,不等待任务的返回结果(异步的结果怎么拿???),直接执行其他代码 (线程池进程池) 异步回...
  • 线程池和内存

    2018-11-25 15:40:24
    文章目录一、线程池1、线程池的概念2、线程池的组成部分3、线程池的流程4、线程池的Demo5、线程池的应用二、线程池的惊群效应1、惊群效应的...的特点四、线程池和内存的相关问题1、线程池大小应该设置为多少...
  • 的概念 由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间,即“浪费”服务器的硬件资源,以换取其运行效率。这就是的概念。是一组资源的集合,这组资源在服务器启动之初...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 27,379
精华内容 10,951
关键字:

线程池技术和进程池技术