Linux下线程的创建(2)

线程的创建

1.线程创建的Linux实现

我们知道,Linux的线程实现是在核外进行的,核内提供的是创建进程的接口do_fork()。内核提供了两个系统调用__clone()和fork (),最终都用不同的参数调用do_fork()核内API。当然,要想实现线程,没有核心对多进程(其实是轻量级进程)共享数据段的支持是不行的,因 此,do_fork()提供了很多参数,包括CLONE_VM(共享内存空间)、CLONE_FS(共享文件系统信息)、CLONE_FILES(共享文 件描述符表)、CLONE_SIGHAND(共享信号句柄表)和CLONE_PID(共享进程ID,仅对核内进程,即0号进程有效)。当使用fork系统 调用时,内核调用do_fork()不使用任何共享属性,进程拥有独立的运行环境,而使用pthread_create()来创建线程时,则最终设置了所 有这些属性来调用__clone(),而这些参数又全部传给核内的do_fork(),从而创建的"进程"拥有共享的运行环境,只有栈是独立的,由 __clone()传入。

Linux线程在核内是以轻量级进程的形式存在的,拥有独立的进程表项,而所有的创建、同步、删 除等操作都在核外pthread库中进行。pthread 库使用一个管理线程(__pthread_manager(),每个进程独立且唯一)来管理线程的创建和终止,为线程分配线程ID,发送线程相关的信号 (比如Cancel),而主线程(pthread_create())的调用者则通过管道将请求信息传给管理线程。

2.线程的创建函数

pthread_create函数
函数简介
   pthread_create是UNIX环境创建线程函数
头文件
   #include<pthread.h>
函数声明
   int pthread_create(pthread_t *restrict tidp,const pthread_attr_t restrict_attr,voidstart_rtn)(void),void *restrict arg);
返回值
   若成功则返回0,否则返回出错编号
参数
   第一个参数为指向线程标识符的指针。
   第二个参数用来设置线程属性。
   第三个参数是线程运行函数的地址。
   最后一个参数是运行函数的参数。
注意
   新的线程从start_rtn函数的地址开始运行
   不能保证新的线程和调用线程的执行顺序
   在编译时注意加上-lpthread参数,以调用静态链接库。因为pthread并非Linux系统的默认库。

2.1案例

下面以龟兔赛跑的故事创建两个线程,并查看运行结果。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
void *thread_function(void* arg);
int main()
{
        int result;
        //定义两个线程标示符指针 乌龟和兔子
        pthread_t rabbit,tortoise;
        //使用pthread_create创建两个线程,并传入距离值50m
        result=pthread_create(&rabbit,NULL,thread_function,(void*)50);
        if(result!=0)
        {
                perror("pthrad creat fail!\r\n");
                exit(EXIT_FAILURE);
        }
        result=pthread_create(&tortoise,NULL,thread_function,(void*)50);
        if(result!=0)
        {
                perror("pthrad creat fail!\r\n");
                exit(EXIT_FAILURE);
        }
        //主控线程调用pthreat_join(),自己会阻塞
        //直到rabbit和tortoise线程结束,方可运行
        /*如果将pthread_join()和sleep(10)都屏蔽了,可查看程序运行的结果。*/
        pthread_join(rabbit,NULL);
        pthread_join(tortoise,NULL);
        //sleep(10);//休眠秒的时间
        printf("Control thread_id=%lx\r\n",pthread_self());
        printf("Finish\r\n");
        return 0;
}
void *thread_function(void *arg)
{
        int i;
        int distance=(int)arg;
        for(i=0;i<=distance;i++)
        {
                printf("p_id=%lx,Run=%d\r\n",pthread_self(),i);
                int time=(int)(drand48()*100000);//产生微妙的随机时间
                usleep(time);//休眠us时间
        }
        return (void*)0;//运行完成之后返回0 表示结束
}
    

pthread_self()函数可获取当前线程的tid_ID。
由于pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,所以在使用pthread_create创建线程时,在编译中要加-lpthread参数:

gcc -o pthread1 thread_creat.c -lpthread

2.2运行结果解析

a.如果将pthread_join()和sleep(10)都屏蔽了,可查看程序运行的结果。

在这里插入图片描述
从运行结果来看,发现新建的两个线程中未运行,程序直接运行到最后的函数。

为什么会出现这样的问题?
解答:在多线程的程序中,主控线程一旦运行结束,所有的相关子线程将也会被强制结束。
其中若加入sleep(10)意思是让主控线程休眠10s,这样两个子线程才得以有时间进行运行。时间太短,也会影响到两个子线程的运行。
若使用pthread_join()函数,此函数功能是被主控线程调用,自己会阻塞,直到rabbit和tortise两个线程都结束,方可运行。

b.正常运行的情况,可查看程序运行的结果。

在这里插入图片描述
从结果来看,两个线程每次运行的顺序都不一样,线程的执行都是由系统调度的,所以不能保证线程每次执行的次序

2.2案例的改进

函数声明
   int pthread_create(pthread_t *restrict tidp,const pthread_attr_t restrict_attr,voidstart_rtn)(void),void *restrict arg);

最后一个参数void *restrict arg是运行函数的参数。可以向创建的线程之中传入需要的参数。将以上案例进行改进:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
void *thread_function(void* arg);
typedef struct{
        char name[20];
        int  time;
        int  start;
        int  end;
}RaceArg;
int main()
{
        int result;
        //定义两个线程标示符指针 乌龟和兔子
        pthread_t rabbit,tortoise;
        //定义兔子的初始结构体
        RaceArg rabbit_arg={"Rabbit", (int)(drand48()*10000000),0,50};
        //定义乌龟的初始结构体
        RaceArg tortise_arg={"Tortise",(int)(drand48()*10000000),10,50};
        //使用pthread_create创建两个线程,并传入距离值50m
        result=pthread_create(&rabbit,NULL,thread_function,(void*)&rabbit_arg);
        if(result!=0)
        {
                perror("pthrad creat fail!\r\n");
                exit(EXIT_FAILURE);
        }
        result=pthread_create(&tortoise,NULL,thread_function,(void*)&tortise_arg);
        if(result!=0)
        {
                perror("pthrad creat fail!\r\n");
                exit(EXIT_FAILURE);
        }
        //主控线程调用pthreat_join(),自己会阻塞
        //直到rabbit和tortoise线程结束,方可运行
        pthread_join(rabbit,NULL);
        pthread_join(tortoise,NULL);
        //sleep(10);//休眠秒的时间
        printf("Control thread_id=%lx\r\n",pthread_self());
        printf("Finish\r\n");
        return 0;
}
void *thread_function(void *arg)
{
        RaceArg *Arg=(RaceArg*)arg;
        int i;
        int time=Arg->time;
        for(i=Arg->start;i<=Arg->end;i++)
        {
                printf("%7s(%lx) Run=%d\r\n",Arg->name,pthread_self(),i);
                usleep(time);//休眠us时间
        }
        return (void*)0;
}

a.查看运行结果,如图示,确认相关的参数被传入至线程的任务当中。
在这里插入图片描述

b.线程中的参数都为局部变量,只在各自线程中有效,数据不共享。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42169059/article/details/89305348