1、需求
让主线程循环创建五个子线程,并让子线程判断自己是第几个子线程。
2、分析
我们只需要利用for循环创建子线程,并且在线程处理函数中打印自己是第几个就OK。
3、代码实现
//循环创建子线程
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
//线程处理函数
void * mythread(void *arg)
{
int i = *(int*) arg;
printf("i == [%d]\n",i);
printf("child thread, pid == [%d], id == [%ld]\n", getpid(), pthread_self());
}
int main()
{
int n = 5;
int i;
int ret;
pthread_t pthread[5];
for(i = 0; i < n; ++i)
{
ret = pthread_create(&pthread[i], NULL, mythread, &i);
if(ret != 0)
{
printf("pthread create error, [%s]", strerror(ret));
return -1;
}
}
printf("child thread, pid == [%d], id == [%ld]\n", getpid(), pthread_self());
sleep(2); //目的是为了让子线程能够执行起来,否则主线程先执行后进程空间被回收,子线程执行不了
return 0;
}
3.1 结果和分析
上述程序打印结果是这样的,并不是我们所期望的结果,那么原因是什么呢?
在创建子线程的时候使用循环因子作为参数传递给子线程,这样主线程和多个子线程就会共享变量i(变量i在main函数中定义,在整个进程都一直有效)所以在子线程看来变量i是合法的栈内存空间。
那么为什么最后有的子线程打印出来的值都是一样的呢?
是由于主线程可能会在一个cpu时间片内连续创建了3个子线程,此时变量i的值变成了3,当主线程失去cpu的时间片后,子线程得到cpu的时间片,子线程访问的是变量i的内存空间的值,所以第一个子进程打印出来值为3。然后主线程又获得了CPU的时间片,又创建了1个子线程,这时候后续两个子进程得到cpu时间片读取到的i是4,所以打印出2个4,最终主线程获取CPU时间片创建出最后一个线程,然后最后两个子线程读到的i值就是5。
总结 出现上述情况根本原因就是主线程和子线程谁先得到CPU时间片去执行的顺序是随机的。
4、改进
知道这种情况的根本原因后,我们可以人为的控制主线程和子线程的先后执行顺序,从而达到预期的结果。
4.1 利用sleep函数
一种就是每次创建出一个子线程,都调用sleep函数让主线程让出它的时间片,我们只需要在创建完一个线程后加个sleep即可。
...
for(i = 0; i < n; ++i)
{
ret = pthread_create(&pthread[i], NULL, mythread, &i);
sleep(1);
if(ret != 0)
{
printf("pthread create error, [%s]", strerror(ret));
return -1;
}
}
...
结果:
4.2 利用独立内存(推荐)
不能使多个子线程都共享同一块内存空间,应该使每个子线程访问不同的内存空间,可以在主线程定义一个数组:int arr[5];,然后创建线程的时候分别传递不同的数组元素,这样每个子线程访问的就是互不相同的内存空间,这样就可以打印正确的值。
...
int arr[5]; //这块栈内存是可以被子线程读取的
pthread_t pthread[5];
for(i = 0; i < n; ++i)
{
arr[i] = i;
ret = pthread_create(&pthread[i], NULL, mythread, &arr[i]);
if(ret != 0)
{
printf("pthread create error, [%s]", strerror(ret));
return -1;
}
}
...
结果
出现乱序是因为我们不知道那个线程先获得CPU的时间片。