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);
	}
}

时间: 2024-10-09 18:05:11

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

UNIX网络编程卷1 服务器程序设计范式7 预先创建线程,以互斥锁上锁方式保护accept

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.预先创建一个线程池,并让每个线程各自调用 accept 2.用互斥锁取代让每个线程都阻塞在 accept 调用之中的做法 //用于维护关于每个线程基于信息的 Thread 结构 typedef struct { pthread_t thread_tid; /* 线程 ID */ long thread_count; /* 处理的连接数 */ } Thread; Thread *tptr

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

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.程序启动阶段创建一个线程池之后仅仅让主线程调用 accept 并把客户连接传递给池中某个可用线程. //用于维护关于每一个线程基于信息的 Thread 结构 typedef struct { pthread_t thread_tid; /* 线程 ID */ long thread_count; /* 处理的连接数 */ } Thread; Thread *tptr; /* Threa

UNIX网络编程卷1 服务器程序设计范式5 预先派生子进程,由父进程向子进程传递套接字描述符

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.只让你进程调用 accept,然后把所接受的已连接套接字"传递"给某个子进程. 这样做就不用因为所有子进程都调用 accept 而需提供上锁保护 2.父进程必须跟踪子进程的忙闲状态,以便给空闲子进程传递新的套接字 typedef struct { pid_t child_pid; /* 子进程的进程 ID */ int child_pipefd; /* 父进程中连接到该子进程

UNIX网络编程卷1 服务器程序设计范式4 预先派生子进程,以线程互斥锁上锁方式保护accept

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.文件上锁文件系统操作,比较耗时 2.线程上锁,不仅适用于同一进程内各线程之间的上锁,也适用于不同进程之间的上锁. 3.在不同进程之间使用线程上锁要求: 1)互斥锁变量必须存放在由所有进程共享的内存区中 2)必须告知线程函数库这是在不同进程之间共享的互斥锁 /* include my_lock_init */ #include "unpthread.h" #include &l

UNIX网络编程卷1 服务器程序设计范式0 迭代服务器

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.迭代 TCP 服务器总是在完全处理某个客户的请求后才转向下一个客户. 2.从进程控制角度看迭代服务器是最快的,因为它不执行进程控制. /* include serv00 */ #include "unp.h" int main(int argc, char **argv) { int listenfd, connfd; void sig_int(int), web_child

UNIX网络编程卷1 服务器程序设计范式1 并发服务器,为每个客户请求fork一个进程

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.传统并发服务器调用 fork 派生一个子进程来处理每个客户 2.传统并发服务器的问题在于为每个客户现场 fork 一个子进程比较耗费 CPU 时间. /* include serv01 */ #include "unp.h" int main(int argc, char **argv) { int listenfd, connfd; pid_t childpid; void

UNIX网络编程卷1 服务器程序设计范式6 并发服务器,为每个客户请求创建一个线程

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.为每个客户请求创建一个线程,以取代为每个客户派生一个子进程 /* include serv06 */ #include "unpthread.h" int main(int argc, char **argv) { int listenfd, connfd; void sig_int(int); void *doit(void *); pthread_t tid; sockl

UNIX网络编程卷1 server程序设计范式1 并发server,为每一个客户请求fork一个进程

本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.传统并发server调用 fork 派生一个子进程来处理每一个客户 2.传统并发server的问题在于为每一个客户现场 fork 一个子进程比較耗费 CPU 时间. /* include serv01 */ #include "unp.h" int main(int argc, char **argv) { int listenfd, connfd; pid_t childp

[转载] 读《UNIX网络编程 卷1:套接字联网API》

原文: http://cstdlib.com/tech/2014/10/09/read-unix-network-programming-1/ 文章写的很清楚, 适合初学者 最近看了<UNIX网络编程 卷1:套接字联网API>, 英文名叫Unix Network Programming啦,后来上网查了查, 一般都叫UNP逼格会高一点, 就像APUE一样. 他们的作者都是W. Richard Stevens. 另外,他也是TCP/IP Illustrated的作者. 靠,看完作者简介,简直崇拜得