线程控制:线程的创建

线程控制

通过代码实现线程的创建/退出/等待/分离
进行线程控制的接口代码,都来自库函数,
也就是说,操作系统没有提供创建线程的接口,大佬们人为的使用库函数封装了一套线程库。
这套封装的线程库函数,提供线程的各种操作。
使用库函数创建一个线程(用户态),本质上线程就会在内核中创建一个轻量化的进程来实现程序的调度。
(pcb是一个task_struct结构体,存储在内核中,使用的是内核空间,想要访问pcb就得切换到内核态运行。)
在这里插入图片描述

创建:

int pthread_create(pthread_t* thread,const pthread_attr_t* attr,void* (*start_routine)(void* ),void* arg);
函数参数:
	thread:输出型参数,用于获取线程id -- 线程的操作句柄tid
	attr:线程属性,用于在创建线程的同时设置线程属性,暂时通常置NULL
	如: 栈大小  栈的溢出缓冲区大小  joinable属性/detach属性  阻塞信号的继承属性
	start_routine:函数指针,就是一个线程的入口函数 -- 线程运行的就是这个函数,函数运行完毕,这个线程就会退出。但主函数是不会等待这个线程执行完毕的。这个函数返回NULL。
	arg:通过线程的入口函数,传递给线程的参数。要作为参数传递给入口函数statr_routine。
返回值:成功返回0,失败返回非0值(错误编号)。

pthread_create接口的使用:

    1 /*=================================================================                                
    2  *
    3  *          这个demo用于线程的创建,体会线程的独立运行
    4  *
    5  *=================================================================*/
    6 
    7 
    8 #include<stdio.h>
    9 #include<unistd.h>     //sleep()
   10 #include<string.h>     
   11 #include<pthread.h>    //线程库接口头文件
   12 
   13 void* thr_start(void* arg)
   14 {
   15   strcpy(arg,"不好意思,你的参数被修改了~\n");
   16   while(1)
   17   {
   18     printf("I am thread~----%s",(char*)str);
   19     sleep(1);
   20   }
   21   return NULL;
   22 }
   23 
   24 
   25 
   26 int main()
   27 {
   28 
   29   //pthread(获取线程id,线程属性,线程入口函数,入口函数参数)
   30   pthread_t tid;      //pthread_t  : unsigned long long 无符号长整形
   31   char ptr[] = "wohenhao~\n";
   32   int ret = pthread_create(&tid,NULL,thr_start,(void*)ptr);
   33   if(ret != 0){
   34     printf("创建线程失败~\n");
   35     return -1;
   36   }
   37   printf("创建线程成功~\n");                                                                       
   38 
   39   while(1)
   40   {
   41     printf("nihaoa~---%s\n",ptr);
   42     sleep(1);
   43   }
   44   //一个主函数中只能出现一个死循环,所以这个循环是走不到的,
   45   //我们可以使用pthread_create创建普通线程,入口函数中写入死循环,
   46   //也就可以展示两个死循环,仅作使用pthread_create接口使用的展示例
   47   while(1)
   48   {
   49     printf("wobuhao~\n");
   50     sleep(1);
   51   }
   52                                                                                                    
   53   return 0;
   54 }
   

代码写好之后我们发现使用gcc编译提示pthread_create未定义,打开man pthread_create发现,
使用时这个接口时,在编译时应该使用-pthread链接。需要链接到pthread链接库。
在这里插入图片描述
注意这里的-l是为了适应平台,实际上直接加上-pthread也可以
在这里插入图片描述

库的链接使用:
	1.将库文件放在指定的目录下:/lib64/usr/lib64
	2.将库文件的路径添加到环境变量 LIBRARY_PATH中
	3.使用gcc -L选项指定库文件所在路径   -l 指定库文件名称。
	gcc默认是动态库	

运行之后我们就可以看到:
在这里插入图片描述
相当于把主线程main函数的局部变量地址传递给了普通线程:经过同一块虚拟地址空间,同一块页表,其实映射的也是同一块物理内存,谁改变了这个物理内存的数据,另一边也就随之改变。
tid:
在上面代码中,我们在创建线程之前,用pthread_t 定义了一个tid作为pthread_create函数的第一个参数。
我们打印一下这个tid:在这里插入图片描述
这是一个无符号长整形的数字。

那么这tid是什么呢?
其实,当我们创建一个线程时,会在虚拟地址空间上的栈区和堆区之间的共享区中开辟一块独属于这个线程的空间,
这块空间保存这个线程独有的数据,(比如线程的栈,线程在用户态的描述)。
这个空间就叫做这个线程的线程地址空间。tid就是这块空间的首地址。
系统通过向thread_create函数传入线程地址空间的首地址tid,来实现对这块空间的访问,从而实现对线程的控制。
所以这个tid在函数的说明中也叫线程的操作句柄。
在这里插入图片描述
这跟我们之前遇到的pid有什么关系/区别呢?
实际上,这两者并没有关联:

pid 是一个轻量级进程id,是内核中task_struct结构体中的id,
		task_struct -> pid : 轻量级进程id,也就是 ps -efL看到的LWP
		task_struct -> tgid: 线程组id,等于主线程id,也就是进程id,在ps -ef中表现为PID
同一个线程组的所有pcb都有一个相同的tgid

当我们程序运行起来时,使用ps-efL查看的PID 和 轻量化进程id与tid没有关系,tid仅用于对线程的操作
在这里插入图片描述

轻量级进程与线程:
在这里插入图片描述

其实轻量级进程和线程之间还是有区别的,在用户态创建一个线程时,由于系统没有提供线程的概念,
我们需要在内核态创建一个pcb,pcb通过找到tid这个空间首地址实现对线程的操作。
只是因为我们在创建一个线程的同时需要在内核中创建一个轻量化的进程,这才把线程叫做轻量化的进程。其实两者是有区别的。
线程是大佬封装的库函数,在用户态创建一个线程,会在内核态创建一个轻量级进程与之对应,
ps -efL 的LWP就是内核中的pcb轻量化进程,只是一一对应,所以也说LWP是线程id号。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值