先看一段代码,观察这段代码可能存在什么问题:
#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来释放申请的空间。