我阅读了关于多线程中使用的套接字的代码,代码如下:
功能主
int main(void)
{
int listenfd;
int i = 0;
/* check envirenment */
if (check_env() == FALSE)
{
return 0;
}
/* get the bind port */
listenfd = initserver(PORT);
if ( listenfd == -1 )
{
return 0;
}
/* initial the message queue. */
/* and start to run ...*/
initDatas(listenfd);
/* start already.........*/
/* make the main thread be a thread which recive the requst from
* client */
fMsgIn((void *)&listenfd);
return 0;
}
函数initDatas
void initDatas(socketfd fd)
{
int num_accept_req = 5;
int num_go = 5;
int num_getblg = 5;
/* control userbuf */
init_userbuf();
/* init the ctrlsockfd list */
init_ctrlsockfd();
/* run server */
init_accept_req(fd, num_accept_req);
/* get blog */
init_getblg(num_getblg);
/* put blog */
// init_pubblg(num_pubblg);
/* get personal msg */
// init_getprsnalmsg(num_getprsnalmsg);
/* pub personal msg */
// init_pubprsnalmsg(num_pubprsnalmsg);
/*get followers */
// init_getfollower(num_getfollower);
/* set personal information */
//init_setprsnalinfo(num_setprsnalinfo);
/* send out dates ...*/
init_msgout(num_go);
}
函数init_accept_req
void init_accept_req(socketfd fd, int number_thread)
{
#ifdef DEBUG
printf("ninitial thread for accept request !n");
ASSERT(number_thread >= 1 && fd > 0);
#endif
pthread_t *pid;
pthread_attr_t attr;
int i = 0;
pid = Malloc_r(number_thread * sizeof(pthread_t));
if ( pid == NULL )
err_quit("malloc, in init_accept_req");
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
for ( i = 0; i < number_thread; i++ )
{
/* control accept requst */
pthread_create(pid + i, &attr, (void *)fMsgIn, (void*)&fd);
}
}
我们可以看到,套接字文件描述符listenfd
是由函数initserver
创建的,在函数init_accept_req
中,创建了多线程,并在这些线程的回调函数中调用了linux套接字函数accept
,即函数fMsgIn
,所以我的问题是,当多线程使用同一个套接字fd时,这些线程之间没有任何冲突吗?(请注意,当调用linux套接字函数accept
时,这些线程中没有同步原语)?
回答问题:
在同一个套接字上列出多个线程确实有效,就像最近的实现一样,accept()
是线程保存。
然而,必须小心检查所有这些并行accept()
的结果,因为在尝试连接的客户端上可能会返回多个tehm,但只有一个accept()
这样做不会出错。
此外,有人可能会认为,由于这些多重回报,这种情况效率低下。
但是这些调用
for ( i = 0; i < number_thread; i++ )
{
pthread_create(pid + i, &attr, (void *)fMsgIn, (void*)&fd);
}
创建线程是潜在的杀手,因为它们向下传递给线程函数对本地变量的引用
void init_accept_req(socketfd fd, int number_thread);
即CCD_ 10。
一旦init_accept_req()
返回,fd
就不再有效,传递给线程函数的引用所指向的也不再有效。
要解决此问题,请向下传递对侦听套接字的引用,如下所示:
void init_accept_req(socketfd * pfd, int number_thread)
{
[...]
for ( i = 0; i < number_thread; i++ )
{
/* control accept requst */
pthread_create(pid + i, &attr, (void *)fMsgIn, (void*) pfd);
}
}
void initDatas(socketfd * pfd)
{
[...]
init_accept_req(pfd, num_accept_req);
[...]
int main(void)
{
int listenfd;
/* initial the message queue. */
/* and start to run ...*/
initDatas(&listenfd);
[...]
使用这种方法,只需要确保main()
确实结束(以便侦听套接字listenfd
保持有效),只要任何接受线程都在做它们的工作。
有点脏的解决方案是将线程函数的void *
类型的用户数据参数滥用为int
,并按如下值传递套接字描述符:
pthread_create(pid + i, &attr, (void *)fMsgIn, (void*) fd);
不好,但只要sizeof(void*)
不小于sizeof(int)
,就可行。
通常情况下,在没有同步的情况下从多个线程对一个套接字进行操作是个坏主意,尽管这并不被禁止。例如,您可以有一个线程读取和一个线程写入。但是,如果您尝试让两个线程都读取,或者两个线程同时写入,那么如果没有任何同步,您可能不会得到合理的结果。类似地,如果您在一个线程中close()
没有同步,您将遇到麻烦,因为另一个线程可能最终访问不存在的FD。
据我所知,在你试图实现的方案中,do有一些同步。当你在主线程中accept
时,你会得到一个新的fd,然后你将其传递给负责它的子线程。在accept
发生后(更重要的是在pthread_create()
输入后),你的主线程保证不会处理该fd。您发布的代码示例并不完整,因此很难判断您是否真的实现了这一点(例如,我看不出在哪里调用accept
)。
Stephens关于高级UNIX编程的书也是这方面的宝贵资源;它提供了许多分叉、非分叉和线程服务器和客户端的示例。