线程的一些概念:
父线程结束 子线程立马同归于尽
— 子线程受到进程级别的打击 整个家族会瞬间爆炸
线程具有进程特性 也具有PID
— 线程有两重身份: 1.线程身份 2.子进程身份
— 线程就是与亲族之间共享资源的轻量级进程, 共享父进程的空间
子线程创建的新线程是它的兄弟: 他们共享空间,身份均等
— 但是会有一个本质上为兄弟线程的父线程用来管理别的线程
线程的栈是私有的
线程的优势是快,轻量级, 它是高速进程
线程是程序调度的基本单位, 进程是资源分配的基本单位(独立的资源空间)
多线程是主进程中衍生的共享进程资源的线程的集合
线程的函数是库函数,是在用户空间运行
ps auxH 来查看线程
pstree -ap也可以查看线程
— 每个线程都具有其私有的PID 用大括号括起来的
线程的tid只在一个进程空间有效,离开进程空间就无效了
— 仅仅是用来标记子线程的信息,给予操作线程的机会
— tid才是我们操作的对象
创建线程:
int pthread_create(pthread_t *tidp,const pthread_attr_t *attr, (void*)(*start_rtn)(void*),void *arg);
tidp: 返回的tid值
attr:线程的状态 一般使用NULL来赋值
start_rtn: 线程执行的函数 对函数的返回值与参数有要求
arg: 函数传入的参数
注意:
线程只能调用 pthread_exit(NULL) 函数来退出, 如果调用exit则瞬间爆炸
线程分离: pthread_detach(pthread_t thread);
将分离的属性与主线程切断联系
在子线程结束后自动回收资源(*唯一的区别!)
— 与之相对的是线程回收程序:pthread_join(pthread_t thread, void *);
不能回收已经分离的回收程序, 这个程序就是给子线程收尸用的
pthread_equal():用来确认是不是给自己发, 防止发错
pthread_cancel():取消线程,是等到合适的时机取消! (取消点)
— 系统函数就有取消点
— pthread_testcancel(void) 人为制造取消点 要加入到函数的重复路径上!
— 分离的子线程也可以被取消
Tips: 迭代与递归的区别? **
循环不一定能被递归替换 反而是可以: 递归调用会占用栈空间,次数太多时会出现错误
pthread_setcanceltype(): 设置如何响应取消
pthread_setcancelstate(): 设置是否能被取消
pthread_sigmask()/kill(): 用法跟原来一样
练习:
1.通过多线程来拷贝文件 实现多线程的CP
确定参数列表中每个文件的大小先, 然后再开始设计创建线程,并分配任务
把CP 的文件的信息保存再结构体中 然后再传入结构体进行线程的拷贝
可以有上限和下限 然后通过合理规则决定线程数量
2.线程池!
一次性创建很多线程,挂起等待命令的到来
每次命令运行的时候,寻找一个池中闲置的线程
运行传入函数的命令
运行完之后再回收
int pthread_pool_create(int num_of_pthread);
--- 线程要活着挂起!
--- 记得设定线程MAX数量
int pthread_pool_mission(void * (*fn)(void *arg), void *arg);
--- 分配任务函数: 分配给线程池中某一个闲的函数
--- 返回值是分配成功与否, no wait!
int pthread_pool_end(void);
--- 想象怎么join掉所有的子进程! lwp?
--- 用返回值告诉用户成功与否!
线程属性:
sysconf(): 获取线程最大数量 return -1: 出错/没有限制
— 线程有栈的大小的上限
设置取消状态是在线程开始运行之后设定的:
pthread_setcancelstate();
pthread_setcanceltypde();
初始化/销毁结构体: pthread_attr_init()/destroy();
栈,栈地址、大小,栈警戒区,分离属性(2个宏)
— 函数在书上或者 man -k pthread_attr
Tips: 并不建议使用线程属性, 系统分配的一定是很好的
2016-1-23:
线程同步技术:
1.线程锁
2.自旋锁
3.读写锁
4.条件变量
类似全局变量, 减少切换内存片段的代价
— 查找进线程的区别(Teacher Blog) 总结条数
线程锁: pthread_mutex_t
1.普通锁: 加锁一次解锁一次 |先等先得
2.嵌套锁: 可以重复加锁,解锁够了才开锁 |自由竞争 (可互斥 可计数)
3.纠错锁: 重复加锁报错,只能由本线程解锁 |先等先得
4.自适应锁:加锁一次 解锁一次 |自由竞争
— 通过函数初始化与销毁锁:
静态初始化: mutex = 宏值
动态初始化:
pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t * attr);
— attr结构体设置
pthread_mutexattr_init(); //初始化
pthread_mutexattr_settype(); //设置属性 才能动态初始化
删除锁之前先锁(失败则挂起等待)后解锁 确保没有人正在使用这个锁
pthread_mutex_lock(); //加锁 出错就挂起
pthread_mutex_trylock(); //加锁 出错就返回
pthread_mutex_timedlock(); //加锁 超时就返回
pthread_mutex_unlock() ; //解锁
穿插加锁是很危险的, 容易造成死锁
在红帽7.0的内核下 pthread库的解锁加锁中改变了 :
挂起的时候发现被解锁会慢一拍,会被剥夺使用CPU的能力
而正在使用锁的人解锁后继续抢夺资源时会立刻抢到锁
为解决这个问题,让使用锁的人usleep(10)一下有利于公平竞争
自旋锁:pthread_spinlock_t
短程占用高频占用式
时刻等待着锁的资源,不停地询问是否锁是空闲的
对锁等待时间短的时候才使用自旋锁
希望快速等待到资源才使用: 对CPU的占用较高!
自旋锁没有种类:但是有是否线程间共享锁的创建参数(PTHREAD_PORCESS_PRIVATE/SHARED)
pthread_spin_lock/trylock/unlock()
读写锁:pthread_rwlock_t
读多写少的场景
一读多读 一写都禁 (为了取得写的权限 则不能有人正在读 也不能有人继续获得写权限)
可以避免饥渴问题, 取决于互斥问题
为了保护读写操作
也有初始化的静态宏值 在/usr/include/pthread.h中有定义 (有读优先和写优先类型)
pthread_rwlock_init/destory/rdlock/wrlock/unlock/tryrdlock/trywrlock..
条件变量:pthread_cond_t
用于不知道何时释放锁的时候,处于盲等状态,使等待成为被动触发的状态
等事件触发之后再进行, 本质是一个全局变量
运用了事件编程的思想
与线程锁配合使用
一旦条件满足就唤醒因等待满足特定条件而睡眠的线程
优势是 等条件的线程们全部在挂起等待 不会消耗内存
类似于线程池的信号系统
pthread_cond_signal(); //唤醒一个线程
pthread_cond_broadcast(); //唤醒所有线程 但是只有一个人拿到锁
//使用流程:
1.pthread_mutex_lock
2.pthread_cond_wait //内部会先解锁 然后等条件再加锁
//保证每一个时间等待条件的人只有一个
//串行挂起 先解锁后的所有人进入此行循环
3.pthread_mutex_unlock
用户校验
1.setuid 设置用户id 再之后的代码以该id身份执行
2.getspnam 传入用户名 返回的是一个带有用户身份的指针(含有密文密码)
— getpwuid 得到uid (更好用)
3.crypt 对比明文与密文
1.模块化 可以拆装 宏内核
2.一个整体内核一块崩溃全体崩溃 微内核 –linux
— 查看两者区别