在《【Linux】fork()》(//m.ajphoenix.com/linux/27062.html)只是简单交代了如同利用fork()创建子线程的方法,实际是更应该说将一个程序一分为二的方法。还有很多事情隐藏在其中值得细致思考。由于fork()结构的特殊性,如果要用fork()创建多个线程,并不像pthread_create()那样轻松,而且最关键的一点,是你利用for循环和fork()创建出来的多线程,会不明不白地多出N条线程。这主要是由于对fork()的理解不够所造成的。比如如下代码:
#include <stdio.h>
int main(){
pid_t pid[2];
int i;
for(i=0;i<2;i++){
if((pid[i]=fork())<0){
printf("Fork() Error!");
}
else if(pid[i]==0){
printf("This is parent %d,child is %d\n",getppid(),getpid());
}
else{
wait(5);
}
}
return 0;
}
看起来好像没有任何问题,创建2条子线程,就是在原有fork()的基础上,建立一个for而已。
但就是不知道会创建出4条线程。那是因为,问题没有想象中的那样简单,父进程现在标号为i=1的循环中创了一个子进程,然后第二次循环,前边的第一个子线程又创建一个子进程,这时明显系统中有四个进程。如下图所示:
因此这种创建多个子进程的方式不可取,是否一定需要用pthread_create呢?其实并不是,可以用如下的结构,创建多个子进程,以要创建4个为例:
#include <stdio.h>
#define MAX_THREAD 4//设置子线程数量
int main(){
int i,status,pid;
for(i=0;i<MAX_THREAD;i++){
status=fork();
if (status==0||status ==-1){
break;
/*每次循环时,
如果发现是子进程就直接从创建子进程的循环中跳出来,
不让你进入循环,
这样就保证了每次只有父进程来做循环创建子进程的工作
*/
}
}
if(status==-1){//一般不会这种情况!
printf("创建的子进程,失败");
}
else if(status==0){//每个子进程都会执行的代码
printf("子线程id=%d,我老爸的id=%d,i=%d\n",getpid(),getppid(),i);
wait(status,NULL,0);
}
else{
printf("父进程id=%d\n",getpid());
while((pid=wait(&status))>0){
printf("终结id为%d的子线程\n", pid);
}
}
return 0;
}
大家需要自己的子进程做什么,直接在else if(status==0){}这个结构里面改就行了。
同时,注意到这里停止子进程与《【Linux】fork()》(//m.ajphoenix.com/linux/27062.html)停止单个子进程是不通。父进程一般不做任何事情,相当于pthread_create里面的主函数,具体见《【Linux】线程》(//m.ajphoenix.com/linux/27065.html),他唯一的职责就是让等待每一个子线程停止。
运行结果如下图:
大家注意到这个运行结果很有意思,i是从3到0倒着来输出的。本身程序就很有意思,if-else if-else这个结构就游离在for循环之外,却如同将这段代码做了4次。
这是因为fork()保存现场,将线程压入栈的特性。i=0时候这个现场被创建的子线程最先入栈,所以他是最后输出的。
if-else if-else这个结构相当于将线程栈里面的线程经过else if中的处理之后一一出栈。
else if中最初能操作的东西,是子线程入栈时候的状态。
大家明白了吗,其实这跟汇编语言里面的循环其实很类似的。
本文永久更新地址://m.ajphoenix.com/linux/27066.html