线程竞争条件和C

时间:2019-01-08 14:45:57

标签: c sockets thread-safety

我不太清楚为什么这个简单的网络代码会阻止竞争状况

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

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

现在,我不确定在pthread_create()内还是在另一时刻调用过程main()时是否复制了该值。在第二种情况下,我会了解比赛情况。

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:2)

您是正确的,不需要动态分配(例如使用malloc)来传递值。

但是 线程函数(和pthread_create)接受 pointer 而不是值,这就是原因大多数问题中,许多问题只是使用地址操作符&来传递指向变量的指针:

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