多线程
线程概念
进行多任务处理
多任务处理:
多创建几个进程,一个进程就有一个pcb,能够串行化的完成一个任务,在一个进程创建多个pcb,因为pcb是调度程序运行的描述,因此有多少个pcb就会有多少个执行流程
多进程
多线程
最早的时候,进程就是一个pcb,操作系统通过pcb>实现调度管理;然而学习多线程,因为线程是进程中的一个执行流,并且这些pcb共用同一个虚拟地址,这些pcb更加轻量化,因此也被称为轻量级进程
在linux中,pcb就是调度程序运行的描述,一个pcb就可以调度一段程序的运行;
创建线程会伴随在内核中创建一个pcb来实现程序的调度,作为进程的一个执行流
进程就是多个线程的集合,并且这个进程中的所有pcb共用进程中的大部分资源(程序运行时,操作系统为程序所分配的所有资源)因此这些pcb在linux称为轻量级进程
线程间独有与共享
独有:栈,寄存器, 信号屏蔽字,errno,标识符,调度优先级
共有:虚拟地址空间,IO信息,信号处理方式,工作目录,用户ID/组ID
为什么信号是先注销在处理?
信号针对整个经常通知时间进行处理的,但是一个信号只需要被处理一次就够了;然而一个进程有可能使用vfork创建一个子进程;父子进程共用一个虚拟地址空间,但父进程会阻塞直到子进程exit或重新替换;
多线程/多进程进行多任务处理的优缺点分析
多线程的优点
1.线程间通信更加灵活方便(处理进程间通信方式还有全局变量以及函数传参----共用一个虚拟地址空间,只要知道地址就能访问同一块空间)
2.线程的创建与销毁成本更低(创建线程创建一个pcb,共用的数据只需要一个指针指向同一处就可以了)
3.同一个进程中的线程间调度成本更低(调度切换需要切换页表…)
多进程的优点
1.多进程的健壮性,稳定性更高(异常以及一些系统调用exit直接针对整个进程生效)
共同的优点
1.IO密集型程序:多任务并行处理
2.CPU密集型程序:程序中进行大量的数据运算处理;cpu资源足够,就可以同时处理,提高效率(通常执行流的个数是cpu核心数+1),创建线程很多的话,而cpu资源不够多,会造成进程切换调度成本提高。
线程控制
通过代码实现线程的创建/ 退出/ 等待/ 分离
使用库函数创建一个线程,本质上是在内核中创建一个轻量级进程来实现程序的调度
线程的创建
int pthread_creat(pthread_t* thread, const pthread_attr_t* attr, void*(start_routine)(void*), void* arg)
参数说明:
thread:输出型参数,用于获取线程id--线程的操作句柄
attr:线程属性,用于在创建线程的同时设置线程属性,通常置NULL
start_routine:函数指针,这就是一个线程的入口函数--线程运行的就是这个函数,函数运行完线程就会退出
arg:通过线程入口函数,传递给线程的参数
返回值:成功返回0;失败返回非0值--错误编号
#include<stdio.h>
#include<unistd.h> //sleep头文件
#include<string.h> //字符串操作头文件
#include<pthread.h> //线程库接口头文件
void* thr_strat(void* arg){
while(1){
printf("nihaoya~%s\n", (char*)arg);
sleep(1);
}
return NULL;
}
int main(){
pthread_t tid;
char ptr[] = "shuilme";
int ret = pthread_create(&tid, NULL, thr_strat, (void*)ptr);
if(ret != 0){
printf("create thread failed!\n");
return -1;
}
printf("create thread success!\n");
while(1){
printf("hello~\n");
sleep(1);
}
}
pcb是一个无符号长整型数据
一个线程就有一个pcb,每一个pcb都有一个pid----pcb是一个整型数据
tid和pid有什么联系
tid ----是一个线程id,线程的操作句柄,准确的来说这个tid就是用户态线程的id,这个id其实就是线程独有这块空间的首地址
每个线程被创建出来之后,都会开辟一块空间,存储自己的栈,自己的描述信息
pid----是一个轻量级进程id内核中的task_struct结构体中的id
task_struct->pid:轻量级进程id,也就是ps -ef看到的LWP
task_struct->tgid: 线程组id,等于主线程id(也就是外边看到的进程id)
线程的终止
如何退出一个线程
1. 线程入口函数运行完毕后,线程就会自动退出----在线程入口函数中调用return(但是main中调用return,退出的是进程还不是主线程)
2.
void pthread_exit(void* retval);
退出线程接口- 谁调用谁退出,retval是退出返回值(exit函数无论在哪个进程调用,退出的都是整个进程)
主线程退出,并不会导致进程退出,只有所有的线程都退出了,进程才退出
3.
int pthread_cancel(pthread_t thread);
终止一个线程;退出的线程是被动取消的
线程的等待
等待一个线程的退出,获取退出线程的返回值,回收线程所占的资源
线程有一个属性,默认创建出来这个属性是joinable,处于这个属性的线程,退出后,需要被其它线程获取返回值回收资源
int pthread_join(pthread_t thread, void **retval)
----等待指定线程退出,获取返回值
参数列表:
thread:要等待退出的线程id
retval:输出型参数,用于返回线程的返回值
线程的分离
将线程joinable 属性改为detach 属性;
一个线程属性若是joinable 那么就必须被等待
一个线程属性若是detach那么这个线程退出后则自动释放资源,不需要被等待(因为资源已经自动释放了)
分离一个线程,一定是你对线程的返回值,不感兴趣,根本就不想获取,又不想一直等待
int pthread_deatch(pthread_t thread);
----将指定线程分离出去,属性改为detach
pthread_t pthread_self(void);
返回调用线程的tid