首页 > 代码库 > UNIX网络编程卷1 服务器程序设计范式8 预先创建线程,由主线程调用accept

UNIX网络编程卷1 服务器程序设计范式8 预先创建线程,由主线程调用accept

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie


1.程序启动阶段创建一个线程池之后只让主线程调用 accept 并把客户连接传递给池中某个可用线程。

//用于维护关于每个线程基于信息的 Thread 结构
typedef struct {
  pthread_t		thread_tid;		/* 线程 ID */
  long			thread_count;	/* 处理的连接数 */
} Thread;
Thread	*tptr;		/* Thread 结构指针,指向一个用 calloc 产生的 Thread结构数组 */


//定义存放已连接套接字描述符的共享数组
#define	MAXNCLI	32
int					clifd[MAXNCLI], iget, iput;
pthread_mutex_t		clifd_mutex;
pthread_cond_t		clifd_cond;


/* include serv08 */
#include	"unpthread.h"
#include	"pthread08.h"


static int			nthreads;
pthread_mutex_t		clifd_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t		clifd_cond = PTHREAD_COND_INITIALIZER;


int
main(int argc, char **argv)
{
	int			i, listenfd, connfd;
	void		sig_int(int), thread_make(int);
	socklen_t	addrlen, clilen;
	struct sockaddr	*cliaddr;


	//1.创建监听套接字
	if (argc == 3)
		listenfd = Tcp_listen(NULL, argv[1], &addrlen);
	else if (argc == 4)
		listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
	else
		err_quit("usage: serv08 [ <host> ] <port#> <#threads>");
	cliaddr = Malloc(addrlen);


	//2.增设一个命令行参数供用户指定预先派生的线程数
	nthreads = atoi(argv[argc-1]);
	tptr = Calloc(nthreads, sizeof(Thread));
	
	//iput 是主线程将往 clifd 数组中存放的下一个元素的下标
	//iget 是线程池中某个线程将从该数组中取出的下一个元素的下标
	iget = iput = 0;


		/* 4create all the threads */
	//3.调用 thread_make 创建各个线程
	for (i = 0; i < nthreads; i++)
		thread_make(i);		/* only main thread returns */
		
	//4.设置中断信号 SIGINT 的处理函数
	Signal(SIGINT, sig_int);


	
	for ( ; ; ) {
		clilen = addrlen;
		//5.阻塞于 accept 等待用户连接
		connfd = Accept(listenfd, cliaddr, &clilen);


		//因为clifd 数组是所有线程共享的,所以要调用 pthread_mutex_lock 和 pthread_mutex_unlock 加以保护
		Pthread_mutex_lock(&clifd_mutex);
		clifd[iput] = connfd; //把已连接套接字存入 clifd 数组的下一个元素
		if (++iput == MAXNCLI) 
			iput = 0;
		if (iput == iget) 
			err_quit("iput = iget = %d", iput);
		//发送信号到条件变量信号
		Pthread_cond_signal(&clifd_cond);
		
		Pthread_mutex_unlock(&clifd_mutex);
	}
}
/* end serv08 */


//中断信号 SIGINT 处理函数
void
sig_int(int signo)
{
	int		i;
	void	pr_cpu_time(void);


	//调用 pr_cpu_time 统计资源利用统计
	//在预先派生子进程的代码中还要先给每个子进程发送 SIGTERM 信号终止它们再统计。
	//这里由于是线程,而子线程与主线程是在同一个地址空间的,当主线程终止时,子线程也会终止。
	pr_cpu_time();


	for (i = 0; i < nthreads; i++)
		printf("thread %d, %ld connections\n", i, tptr[i].thread_count);


	exit(0);
}


void
thread_make(int i)
{
	void	*thread_main(void *);


	//创建线程并使之执行 thread_main 函数,该函数的唯一参数是本线程在 Thread 结构数组中的下标
	Pthread_create(&tptr[i].thread_tid, NULL, &thread_main, (void *) i);
	return;		/* main thread returns */
}


void *
thread_main(void *arg)
{
	int		connfd;
	void	web_child(int);


	printf("thread %d starting\n", (int) arg);
	for ( ; ; ) {
		//因为clifd 数组是所有线程共享的,所以要调用 pthread_mutex_lock 和 pthread_mutex_unlock 加以保护
    	Pthread_mutex_lock(&clifd_mutex);
		//若两者相等则无事可做,调用 pthread_cond_wait 在睡眠在条件变量上,等待主线程唤醒
		while (iget == iput)
			Pthread_cond_wait(&clifd_cond, &clifd_mutex);
		//得到要处理的套接字描述符
		connfd = clifd[iget];	/* connected socket to service */
		if (++iget == MAXNCLI)
			iget = 0;
		Pthread_mutex_unlock(&clifd_mutex);
		tptr[(int) arg].thread_count++;


		//处理客户请求
		web_child(connfd);		/* process request */
		//关闭已连接套接字
		Close(connfd);
	}
}

UNIX网络编程卷1 服务器程序设计范式8 预先创建线程,由主线程调用accept