【问题标题】:socket used in multithreading?多线程中使用的套接字?
【发布时间】:2014-03-14 23:29:42
【问题描述】:

我看了多线程中使用socket的代码,代码如下:

主函数

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

函数初始化数据

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

我们可以看到socket文件描述符listenfd是由函数initserver创建的,在函数init_accept_req中创建了多线程,在这些线程的回调函数中调用了linux socket函数accept,即function fMsgIn,所以我的问题是,当多线程使用相同的套接字 fd 时,这些线程之间没有任何冲突吗?(请注意,当调用 linux 套接字函数 accept 时,这些线程中没有同步原语)?

【问题讨论】:

  • 这些代码 sn-ps 不包含您询问的关键功能。但是相信这些功能会按照您的描述进行,这是安全的。只有一个线程可以接受同一个连接,并且每个线程都是一个单独的套接字。
  • Is accept() thread-safe? 的可能重复项
  • OT:为什么和什么是socketfd

标签: c linux multithreading sockets


【解决方案1】:

回答问题:

在同一个套接字上列出多个线程确实有效,因为最近的实现accept() 是线程保存。

但是,必须注意检查所有这些并行 accept()s 的结果,因为多个 tehm 可能会在尝试连接的客户端上返回,但只有一个 accept() 这样做不会出错。

Also one could argue this scenario is inefficient, due to those multiple returns.


但是这些调用

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

fd

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) 就可行。

【讨论】:

  • fd的来源是函数main中的listenfd,它不是init_accept_req中的局部变量
  • @Charles0429:C 号是按值传递。 fd 存在于 init_accept_req() 的堆栈中。当init_accept_req() 返回时,这个堆栈就消失了。取消引用 &amp;fd 随后返回的内容会引发未定义的行为。
  • 好的,这是这段代码的问题。但关心的是,是否可以同时在多线程中调用函数accept
【解决方案2】:

通常情况下,在不同步的情况下从多个线程对一个套接字进行操作是一个坏主意,尽管它不被禁止。例如,您可以有一个线程读取和一个线程写入。但是,如果您尝试让两个线程都读取,或者两个线程都写入,那么如果没有任何同步,您可能无法获得合理的结果。同样,如果你在一个线程中close() 没有同步,你会遇到麻烦,因为另一个线程最终可能会访问一个不存在的 FD。

据我了解,在您尝试实现的方案中,确实有某种同步。当您在主线程中accept 时,您将获得一个新的 fd,然后您将其传递给其负责的子线程。在发生accept 之后(更重要的是在输入pthread_create() 之后),您的主线程保证不会对该fd 执行任何操作。您发布的代码示例不完整,因此很难判断您是否真正实现了这一点(例如,我看不到 accept 被调用的位置)。

Stephens 关于高级 UNIX 编程的书也是这类事情的宝贵资源;它包含许多分叉、非分叉和线程服务器和客户端的示例。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-03
  • 2011-02-11
  • 2020-03-28
  • 2016-07-23
相关资源
最近更新 更多