多线程server应注意问题

先看一段代码,观察这段代码可能存在什么问题:

#include "unpthread.h"



static void *doit(void *); /* each thread executes this function */


int
main(int argc, char **argv)
{
int listenfd, connfd;
pthread_t tid;
socklen_t addrlen, len;
struct sockaddr *cliaddr;


if (argc == 2)
listenfd = Tcp_listen(NULL, argv[1], &addrlen);
else if (argc == 3)
listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
else
err_quit("usage: tcpserv01 [ <host> ] <service or port>");


cliaddr = Malloc(addrlen);


for ( ; ; ) {
len = addrlen;
connfd = Accept(listenfd, cliaddr, &len);
Pthread_create(&tid, NULL, &doit, (void *) connfd);
}
}


static void *
doit(void *arg)
{
Pthread_detach(pthread_self());
str_echo((int) arg); /* same function as before */
Close((int) arg); /* done with connected socket */
return(NULL);

}

码如上所示,没来一个新链接服务器将新建一个线程来为其服务。但这段代码中存在一个问题:在新建线程的时候,我们将整数变量connfd强制转换成void指针,这本来是没有什么问题的,但不能保证在所有系统上工作。要正确处理这个问题需要做一些额外工作。
首先注意到,我们不能把connfd的地址传递给新线程,也就是说下面代码是有问题的:
int main(int argc,char **argv)
{
 int listenfd,connfd;
 ........
 for(; ; )
{
 len=addrlen;
 connfd=Accept(listenfd,cliaddr,&len);
 Pthread_create(&tid;BULL;&doit,&connfd);
}
}
static void * doit(void * arg)
{
 int connfd;
 connfd=*((int*)arg);
 Pthread_detach(pthread_self());
 str_echo(connfd);
 Close(connfd);
 return (NULL);
}
从ANSI C角度看,这段代码没有问题;我们能够保证将整数指针强制为void*,然后再将该指针强制转会整数指针。问题是这个指针指向什么。
主线程中有一个整数变量connfd,每次调用accept时,新值(已连接套接字)都会覆盖改变量。因此可能会发生以下情况:
(1)accept返回,新的已连接套接字(假设5)存入connfd,主线程调用pthread_create,指向connfd的指针(不是connfd的内容)是pthread_create的最后一个参数。
(2)创建一个线程,并调度doit函数开始运行。
(3)另一个连接准备好,主线程在此运行。accept返回,新的描述字(假设6)存入connfd,主线程调用pthread_create。
即使在此情况下创建了两个线程,但是这两个线程都将在已存入connfd的已连接套接字描述字6上操作。问题的症结在于,多个线程同时存取一个共享变量(connfd)而缺乏同步机制。在原始代码中我们给pthread_create传递connfd的值,而不是指向该值的指针来解决问题。下面给出解决此问题的更好办法:

#include "unpthread.h"


static void *doit(void *); /* each thread executes this function */


int
main(int argc, char **argv)
{
int listenfd, *iptr;
thread_t tid;
socklen_t addrlen, len;
struct sockaddr *cliaddr;


if (argc == 2)
listenfd = Tcp_listen(NULL, argv[1], &addrlen);
else if (argc == 3)
listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
else
err_quit("usage: tcpserv01 [ <host> ] <service or port>");


cliaddr = Malloc(addrlen);


for ( ; ; ) {
len = addrlen;
iptr = Malloc(sizeof(int));
*iptr = Accept(listenfd, cliaddr, &len);
Pthread_create(&tid, NULL, &doit, iptr);
}
}


static void *
doit(void *arg)
{
int connfd;


connfd = *((int *) arg);
free(arg);


Pthread_detach(pthread_self());
str_echo(connfd); /* same function as before */
Close(connfd); /* done with connected socket */
return(NULL);
}

每当调用accept时,我们首先调用malloc给一个整型变量(已连接描述字)分配空间。这使得每个线程都有自己的描述字,从而避免了公用描述字带来的麻烦。线程获得描述字后,一定要用free来释放申请的空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值