我不太清楚为什么这个简单的网络代码会阻止竞争状况
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()
时是否复制了该值。在第二种情况下,我会了解比赛情况。
答案 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;
...
}