Fork me on GitHub

竞争条件

竞争条件

当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,我们认为发生了竞争条件(race condition)。

如果再fork之后的某种逻辑显式或隐式地依赖于在fork之后是父进程先运行还是子进程先运行,那么fork函数就会是竞争条件活跃的滋生地。通常,我们不能预料哪一个进程先运行。即使我们知道哪一个进程先运行,在该进程开始运行后所发生的事情也依赖于系统负载以及内核的调度算法。

如果一个进程希望等待一个子进程终止,则它必须调用wait函数中的一个(子进程终止前,父进程被阻塞了)。如果一个进程要等待其父进程终止,则可使用下列形式的循环:

1
2
3
while(getppid() != 1){
sleep(1);
}

这种形式的循环称为轮询(polling),它的问题是浪费了CPU时间,因为调用者每隔一秒都被唤醒,然后进行条件测试。

为了避免竞争条件和轮询,在多个进程之间需要有某种形式的信号发送和接收方法,在UNIX中可以使用信号机制,各种形式的进程间通信(IPC)也可使用

示例:程序输出两个字符串:一个由子进程输出,另一个由父进程输出。因为输出依赖于内核使这两个进程的运行的顺序及每个进程运行的时间长度,所以该程序包含了一个竞争条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "head.h"
static void charatatime(char *);
int main(void){
pid_t pid;
if((pid = fork()) <0){
printf("fork error\n");
}else if(pid == 0){ //child
charatatime("output for child\n");
}else{ //parent
charatatime("output for parent\n");
}
exit(0);
}
static void charatatime(char *str){
char *ptr;
int c;
setbuf(stdout,NULL); //set unbuffered
for(ptr = str; (c=*str++) != '\0';){
putc(c,stdout);
}
}

在程序中将标准输出设置为不带缓冲的,于是每个字符输出都需调用一次write(内核系统调用)。本例的目的是使内核能尽可能多次地在两个进程之间进行切换,以便演示竞争条件。

1
2
3
4
5
6
7
8
9
10
11
12
[vrlive@iZ23chs2r19Z eight]$ ./race.o
ouotputpuut for child
t for parent
[vrlive@iZ23chs2r19Z eight]$ ./race.o
output for parent
output for[vrlive@iZ23chs2r19Z eight]$ child
./race.o
ououtputtput for child
for parent
[vrlive@iZ23chs2r19Z eight]$ ./race.o
output for parent
output for child
-------------本文结束感谢您的阅读-------------

本文地址:http://www.wangxinri.cn/2018/05/14/竞争条件/
转载请注明出处,谢谢!

梦想夹带眼泪,咸咸的汗水!