Linux TCP server系列(6)-select模式下的多线程server
目标:
修改上一篇的select模式下的server,让它使用多线程来处理客户端请求(多进程的模式已经在上篇中加了注释)。
思路:
(1)服务器
我们已经在之前的客户端模型多个并发用户的过程中使用过多线程的技术了(其中还涉及到多线程利用条件变量进行线程同步),在这里我们可以很轻松的在上篇文章代码中加入线程部分代码。
//for thread
int *lptr;
pthread_t pid;
//for thread
for(i=0;i<=maxi;i++)
{
if( (sockfd=client[i]) <0)
continue;
if(FD_ISSET(sockfd,&rset))
{
//thread client
lptr=(int*)malloc(sizeof(int));
*lptr=sockfd;
int errerr=pthread_create(&pid,NULL,threadPerClient,lptr);
if(errerr!=0)
printf("err %s",strerror(errno));
FD_CLR(sockfd,&allset); //短连接后关闭socket,服务器发不过来
client[i]=-1;
printf("can read : %d,%d,%d\n",i,sockfd,nready);
if(--nready<=0)
break;
//thread client
}
}
但是!!!!真的那么轻松就可以加入吗? 为什么我不直接在pthread_create中传入sockfd,而是要再new一个整型数然后赋值给它再传递过去呢?
答案很明显,如果直接使用sockfd的指针,那么在下次sockfd被client[i]赋值时,sockfd的值变了!而这时线程的实际操作的正是这个sockfd(不只因为线程共享变量,同时还因为传递的是指针)!所以我们复制一个新值,让线程自己处理对应的fd。
这样就完了?很明显不会那么简单,让我们看看线程处理函数就知道。
void* threadPerClient(void *arg)
{
int connfd=*((int*)arg);
free(arg); //防止内存泄露
pthread_detach(pthread_self());
printf("client from %d(socket num)\n",connfd);
str_echo(connfd);
close( connfd );
return NULL;
}
看到注释了吗?“防止内存泄露“!!这是很容易发生的事,在刚才的代码中new一个新对象,那么我们就必须在使用后释放。除此以外我们还注意到这里调用了pthread_detach函数,因为线程分为joinable和unjoinable两种。
如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当你调用了pthread_join之后这些资源才会被释放。若是unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。
unjoinable属性可以在pthread_create时指定,或在线程创建后在线程中pthread_detach自己, 如:pthread_detach(pthread_self()),将状态改为unjoinable状态,确保资源的释放。或者将线程置为 joinable,然后适时调用pthread_join.
(其实就类似于进程退出时父亲进程的wait)
(2)客户端
无需修改
代码:
作者: Aga.J
出处: http://www.cnblogs.com/aga-j
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
个人学习笔记仅供本人记录知识所用,不属发表性文章。