为什么线程函数的参数应该在堆中?

时间:2017-09-03 06:01:13

标签: c multithreading pthreads

我使用pthread库在c中编写了一个多客户端服务器。当每个客户端尝试连接到服务器时,每个客户端作为单独的线程运行,并使用handle_client函数处理每个客户端。

我想知道为什么我需要将connfd声明为堆中的变量?如果将它声明为局部变量,可能会出现什么问题?

这是制作每个线程的代码(在main()函数中)

int* connfd;
pthread_t thread_id;
int client_sock

while (1)
{
    connfd = malloc(sizeof(int));
    *connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen);

    if( pthread_create( &thread_id , NULL , handle_client , (void*)&client_sock) < 0)
    {
        perror("could not create thread");
        return 1;
    }
}

这是我的hadle_client函数。

void* handle_client(void* connfd)
{
    /* read a string sent by the client, 
     * print it and then send the string 
     * "Hello from the server" to the client*/

   int sock = *(int*)connfd;
   int read_size;
   char *message , client_message[2000];

   //Send some messages to the client
    message = "Hello from the server\n";
    write(sock , message , strlen(message));

   while( (read_size = recv(sock , client_message , 2000 , 0)) > 0 )
   {
      //end of string marker
        client_message[read_size] = '\0';

        //Send the message back to client
      puts(client_message);

        //clear the message buffer
        memset(client_message, 0, 2000);
    }

    if(read_size == 0)
    {
        puts("Client disconnected");
        fflush(stdout);
    }

    else if(read_size == -1)
    {
        perror("recv failed");
    }

    free(connfd);
    return NULL;
}

3 个答案:

答案 0 :(得分:1)

为使代码正常工作,有必要确保:

  • 包含连接句柄的变量(在您的代码中,*connfd)存在,只要handle_client线程需要;
  • 其值不会被while循环的后续迭代覆盖。

使用堆变量更容易实现这一点,就像在代码中完成一样。

通常,将本地(基于堆栈的自动)变量的地址传递给线程函数并不一定是错误的。它比使用基于堆的变量需要更多的关注。

答案 1 :(得分:1)

有问题

  • 可能是变量的生命周期在它在线程中使用之前结束。在生命周期之后访问对象具有未定义的行为
  • 在C11之前,没有为从其他线程访问本地变量指定任何行为。由于行为未定义,因此隐式未定义
  • C11表示从另一个线程访问自动变量的行为是实现定义

      

    声明标识符没有链接且没有存储类说明符static的对象具有自动存储持续时间,一些复合文字也是如此。尝试从与对象关联的线程以外的线程间接访问具有自动存储持续时间的对象的结果是实现定义的。

    您是否阅读过编译器手册?

    海湾合作委员会说:

      

    支持此类访问,对并发访问的同步要求与对任何对象的并发访问的要求相同。

  • 即使支持访问,也有可能发生数据竞争,包括编译器决定不再需要该值。

这些点的

适用于动态分配的对象,而在另一个线程启动后未修改的对象。

答案 2 :(得分:1)

如果使用局部变量,它将在主线程的堆栈中初始化。将该变量的地址传递给相应的线程是非常安全的,因为主线程的堆栈变量的生命周期显然足够长。 但问题这样做是为了在同一局部变量中每次更新while(1)中的fd值,这将使每个线程都使用,导致未定义的行为。

由于这个原因,你必须为每个线程在堆或堆栈中分配新变量,这样每个线程都应该能够读取正确的fd值而不会有任何歧义。