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 结果和分析

alt 上述程序打印结果是这样的,并不是我们所期望的结果,那么原因是什么呢?
在创建子线程的时候使用循环因子作为参数传递给子线程,这样主线程和多个子线程就会共享变量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;
        }          
    }
	...

结果:
alt

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

结果 alt

出现乱序是因为我们不知道那个线程先获得CPU的时间片。