苏州建站公司 诚找苏州聚尚网络,网站是怎么制作出来的,新开神途手游发布网站,关键词诊断优化全部关键词线程 为什么有了进程还需要线程 进程切换的时候会花费很大的代价 #xff08;1#xff09;上下文切换#xff0c;CPU寄存器需要切换 #xff08;2#xff09;虚拟地址和物理地址的映射需要切换 进程间通信麻烦 线程是轻量级的进程
#xff08;1#xff09;线程是一个正…线程 为什么有了进程还需要线程 进程切换的时候会花费很大的代价 1上下文切换CPU寄存器需要切换 2虚拟地址和物理地址的映射需要切换 进程间通信麻烦 线程是轻量级的进程
1线程是一个正在执行的程序但是它不在是资源分配的最小单位
2同一个进程存在多个线程多个线程共享内存资源
3线程也有上下文状态主要的是PC指针和stack(栈)指针 用户级线程不能够被CPU感知到的CPU不能根据用户使用线程的多少来进行调度用户自己分配这个进程内部各个CPU使用情况线程调度由进程处理 内核级线程线程调用是由操作系统处理 CPU调度以线程为单位 原来的进程都是当线程进程 在Linux操作系统中进程控制块和线程控制块合二为一了每个进程和线程都有task_struct的描述 引入线程的好处
1减少了上下文切换的代价
2消灭了页表切换
3线程是共享内存的可以无痛通信
创建线程
当我们启动进程就会自动创建一个主线程主线程栈区从main函数开始压栈
在主线程中可以使用pthread_create函数创建一个子线程 pthread_t *thread:线程ID不同操作系统中pthread的实现是不一样的 pthread_attr *attr:线程的属性,填NULL表示我们使用默认属性 void *(*start routine) (void *):线程启动函数参数和返回值都是void*类型-----void *func(void*) 子线程的main函数 void *arg:传递给start_routine的参数
获取自己线程的线程ID Makefile文件后面添加-pthread 让main主线程创建一个子线程让主线程和子线程输出自己的线程ID 如果我们让父线程sleep(1)最终只会打印main主线程的线程ID因为当主线程main,也就是进程终止了那么这个进程里面的线程也就无法运行了 如果我们让主线程只睡20微秒那么我们打印输出的记录有可能由三条记录也有可能只有两条记录因为数据从输入到展示在命令框要经过三个步骤首先是printf将数据拷贝到stdoutstdout将数据拷贝原子操作到内核的文件对象然后再清空我们的stdout,因此主线程有可能在清空stdout之前就终止这时命令框已经输出一次数据然后发现stdout的数据还存在就会把stdout的数据在刷新一遍在显示框这就出现打印三次结果 多线程下不能使用perror 一个典型的报错会做两件事 1return -1 2修改全局变量errno 然后perror会根据errno生成错误提示字符串,所以perror依赖的数据就会存储在数据段里面但是多个和线程都可以同时共同访问一个数据段但是此时如果由多个进程报错其他线程报错信息会覆盖本线程的包i错信息因此就不能获取到正确的错误信息 在多线程中报错不会返回-1而是返回数值通过返回值数值来确定报错的类型 strerror可以通过传入的数值返回给我们一个错误提示字符串 唯一的坏处是它不能打印这个字符串因此我们需要通过 fprintf(stderr)来将错误信息输出 检测我们的进程能够创建多少线程
多线程共享内存空间
多线程可以共享同一个数据段
多线程共享堆空间主线程和子线程使用同一个数值的地址使用pthread_create传递地址参数其实我们是直接把第四个参数拷贝到另外一个线程的栈帧里面 多线程之间传递一个整数直接传递一个long类型的数据因为void *是8个字节long类型也是8个字节不会有信息丢失如果我们希望主线程和子线程之间共享内存那么就传递指针如果不希望共享内存你那么久传递long类型。void *既可以当指针用也可以当long来用 多线程的栈区是相对独立的一个线程可以通过地址区访问另一个线程的栈区
线程的终止 一个进程中的任意一个线程只要触发其中任意一个信号那么整个进程就会终止其中所有的线程也都会终止 1main 线程的return函数、2exit命令、3_exit/_Exit 、4abort、5收到导致进程终止的信号 子线程终止自己 1从threadFunc中return尽量不要用 2pthread_exit主线程不要调用
void *retval子线程的返回值 pthread_join回收线程的资源
join等待任何一个线程的终止
pthread_t thread目标线程的tid(不是一个指针) void **retval是拷贝子线程的终止状态主调函数中申请void * 的内存join试图修改主调函数中的void *
join和exit的例子 pthread_join的的错误用法 多线程和信号不能同时使用
多线程会共享注册信号的信息用户无法得知是哪个线程递送信号
线程的取消类似于信号线程可以在运行过程中给别的线程发送一个取消请求另一个线程收到取消请求之后他不会立刻终止他会将自己的取消标志置为1运行到一些特殊函数的时候就会取消在一些特殊函数之前或者调用之后就会取消进程这些特殊的函数称之为取消点 取消点函数 1操作文件的系统调用、2可能引发阻塞的系统调用库函数和系统调用都是取消点函数 pthread_mutex_lock加锁不是取消点
如果线程是被pthread_cacel终止的并且取消成功那么终止的线程返回值为-1
但是如果线程没有碰到取消点函数那么也就不会执行终止指令会继续执行下去
ps -elLf|grep pthread_cancel查看线程运行状态
手动增加取消点 pthread_testcancel如果取消标志位为真就终止本线程
异步终止可能会导致资源泄漏因为malloc和free函数都是取消点我们不知道pthrread_cancel函数实在malloc之前还是free之后或者是在malloc和free之间这就很容易造成资源泄漏。 因此在目标线程运行到取消点函数和取消点函数调用完前终止线程之间会调用线程中终止清理函数
资源清理栈自动根据申请了多少资源就释放多少资源
当我们申请资源malloc ; open/fopen/opendir ; semop/mutex_lok对应的释放行为 free ; close/fclose/closedir ; semop/mutex_unlock,
我们会去维护一个特殊的结构资源清理栈里面存储了资源释放的行为当我们申请资源之后就会把对应的释放行为压栈pthread_cleanup_push
当线程因为1pthread_exit不包括在启动函数中return、2被cancel终止时将栈清空 线程可以主动调用pthread_cleanup_pop释放资源