线程争用条件和按 C 中的值传递



我不太明白为什么这个简单的网络代码会阻止竞争条件

int main(void) {
   ...
   listen(sd, SOMAXCONN);
     for(;;) {
              pthread_t t; int *socket;
              socket = malloc(sizeof(int));
              *socket = accept(sd, NULL, NULL);
              pthread_create(&t, NULL, service_request, socket);
              pthread_detached(&t);
     }
  ...
}

我得到的解释是,套接字描述符值必须在堆中分配,以避免另一个接受覆盖其值的竞争条件。我不明白的是为什么。如果我只是写:

int main(void) {
   ...
   listen(sd, SOMAXCONN);
     for(;;) {
              pthread_t t; int socket;
              socket = accept(sd, NULL, NULL);
              pthread_create(&t, NULL, service_request, socket);
              pthread_detached(&t);
     }
  ...
}

我按值传递套接字描述符"套接字"。无论如何,下一次接受将在 for 循环的另一个迭代中调用pthread_create()并且复制"socket"的值之后发生。

现在我不确定在 main() 中调用过程 pthread_create() 时还是在另一个时刻复制该值。在第二种情况下,我会理解竞争条件。

pthread_create()将指针参数传递给启动例程。您只需传递一个整数。因此,您假设 filedescriptor 整数可以(隐式)强制转换为指针值并返回而不会损坏。这可能是正确的,因为文件描述符通常低于 16 位,指针值的大小通常为>= 16 位,但它仍然丑陋且容易出错。

因此,malloc()版本是首选,如第一个示例所示。

他们对"竞争条件"的意思是这个版本:

 for(;;) {
          pthread_t t; int socket;
          socket = accept(sd, NULL, NULL);
          pthread_create(&t, NULL, service_request, &socket);
          pthread_detached(&t);
 }

在这里,启动例程必须在下一次接受发生之前评估文件描述符值。否则,旧值将被覆盖,不能再使用。

你是对的,你不需要动态分配(例如使用 malloc ) 要传递的值。

然而 ,线程函数(和pthread_create)接受指针而不是值,这是大多数问题的原因,因为许多人只是使用地址运算符&将指针传递给变量:

pthread_create(&t, NULL, service_request, &socket);

这确实会导致争用条件,因为在线程函数取消引用指针并使用该值之前,socket的值可能会发生变化。

但是对于简单的整数(如示例中的socket变量),有一个简单的解决方法,不包括使用malloc:将值强制转换为指针。这实际上是为数不多的可以进行此类铸造的场合之一:

pthread_create(&t, NULL, service_request, (void *) (intptr_t) socket);

在线程函数中,您执行相反的转换:

void *service_request(void *pointer)
{
    int socket = (int) (intptr_t) pointer;
    ...
}

最新更新