线程
守护进程
守护进程的特点
后台服务进程
独立于控制终端
周期性执行某任务
不受用户登录注销影响
一般采用以d结尾的名字(服务)
进程组
进程的组长
组里边的第一进程
进程组的ID==进程中的组长的ID
进程中组长的选择
进程中的第一个进程
进程组ID的设定
进程组的ID就是组长的进程ID
会话
创建一个会话注意事项
不能是进程组长
创建会话的进程成为新进程组的组长
有些lInux版本需要root权限执行此操作
创建出的新会话会丢弃原有的控制终端
一般步骤;fork ,父亲死,儿子执行创建会话操作(setid)
获取进程所属的会话ID
pid_t getsid(pid_t pid);
创建一个会话
pid_t setid(void);
创建守护进程模型
fork子进程,父进程退出
子进程创建新会话
改变当前工作目录chdir
重设文件掩码
关闭文件描述符
执行核心工作
线程的概念
共享:
.text
.bss
.data
堆
动态加载区
环境变量
命令行参数
-通信:全局变量,堆
不共享
一共五个线程,栈区被平均分成五块
在Linux下: 线程就是进程-轻量级进程
对于内核来货,线程就是进程
多进程和多线程的区别:
多进程: 始终共享的资源 代码、文件描述符、内存映射区--mmap
多线程:始终共享的资源:堆、全局变量,节省资源安卓线程man page ,命令:
sudo apt-get install manpages-posix-dev
查看指定线程的LWP号:
线程号和线程ID是有区别的
线程号是给内核看的
查看方式
找到程序的进程ID
ps -Lf pid
线程的创建
单个线程退出 --pthread_exit
阻塞等待线程退出,获取线程退出状态--pthread_join
线程分离--pthread_detach
杀死(取消)线程--pthread_cancel
int pthread_cancel(pthread_t pthread);
比较两个线程ID是否相等(预留函数) --pthread_equal
int pthread_equal(pthread_t t1,pthread_t t2);
线程同步
数据混乱
操作了共享资源
CPU调度问题
互斥量(互斥锁)
如果没有锁的话,就会出现以下情况
加锁一个执行完再执行另一个
原子操作
CPU处理一个指令,进程/线程 在处理完这个指令之前是不会失去CPU的
就像原子被认为是不可分割颗粒一样
死锁
造成死锁的原因
1.自己锁自己
for(int i = 0;i<MAX;i++)
{
pthread_mutex_lock(&mutex);
pthread_mutex_lock(&mutex);
int crt = number;
crt++;
number = crt;
printf("thread A id = %ld,number = %d\n",pthread_self(),number);
pthread_mutex_unlock(&mutex);
usleep(10);
}
操作做完之后,一定要解锁
2.不放弃自己手中的
线程1 对共享资源A加锁成功-A锁
线程2 对共享资源B加锁成功-B锁
线程1访问共享资源B,对B锁加锁-线程1阻塞在B锁上
线程2访问共享资源A,对A锁加锁-线程2阻塞在A锁上
如何解决
-让线程按照一定的顺序去访问共享资源-在访问其他锁的时候,需要先将自己的锁解开
--try_lock
读写锁
读写锁是几把锁?
一把锁
pthread_rwlock_t lock;
读写锁的类型
读锁-对内存做读操作
写锁-对内存做写操作
读写锁的状态
读写锁的特性
线程A加读锁成功,又来了三个线程,做读操作,可以加锁成功
读共享-并行处理
线程A加写锁成功,又来了三个线程,做读操作,三个线程阻塞
写独占
线程A加读锁成功,又来了B线程加写锁阻塞,又来了C线程加读锁阻塞
读写不可以同时进行
写的优先级高
读写锁场景练习
线程A加写锁成功,线程B请求读锁
线程B阻塞
线程A持有读锁,线程B请求写锁
线程B阻塞
线程A拥有读写,线程B请求读锁
线程B加锁
线程A持有读锁,然后线程B请求写锁,然后线程C请求读锁
线程B阻塞,线程C阻塞
线程B加锁,线程C阻塞
线程C加锁
线程A持有写锁,然后线程B请求读锁,然后线程C请求写锁
线程B阻塞,线程C阻塞
线程C加锁, 线程B阻塞
线程B加锁
读写锁的适用场景
互斥锁-读写串行
读写锁:
读:并行
写:串行
程序中的读操作 > 写操作的时候
代码实现
结果显示
条件变量
条件变量
阻塞线程
不是什么时候都能阻塞线程链表头节点
Node*head = NULL;
while(head == NULL)
{
//我们想让代码在这个位置阻塞
//等待链表中有了节点之后再继续向下运行
//使用到了后面要讲的条件变量‐阻塞线程
}
//链表不为空的处理代码
xxxx
1.条件变量是锁吗?
不是锁,但是条件变量能够阻塞线程
使用条件变量+互斥量
互斥量:保护一块共享数据
条件变量:引起阻塞
生产者和消费者模型
2.条件变量的两个动作
条件不满足,阻塞线程
当条件满足,通知阻塞的线程开始工作
3.条件变量的类型
pthread_cond_t cond ;
conditon 条件
4.主要函数:
初始化一个条件变量
pthread_cond_init(pthread_cond_t * restrict cond,
const pthread_condattr_t * restrict attr
);
销毁一个条件变量
pthread_cond_destroy(pthread_cond_t * cond);
阻塞等待一个条件变量
pthread_cond_wait(
pthread_cond_t *restrict cond,
pthread_mutex_t * restrict mutex
);
阻塞线程
将已经上锁的mutex解锁
该函数解除阻塞,对互斥锁加锁限时等待一个条件变量
pthread_cond_timedwait(
pthread_cond_t * restrict cond,
pthread_mutex_t * restrict mutex,
const struct timespec * restrict abstime
);
唤醒至少一个阻塞在条件变量上的线程
pthread_cond_signal(pthread_cond_t* cond);
唤醒全部阻塞在条件变量上的线程
pthread_cond_broadcast(pthread_cond_t * cond);
练习
使用条件变量实现生产者,消费者模型
信号量
1.头文件-semaphore.h
2.信号量类型
sem_t sem;
加强版的互斥锁
3.主要函数
初始化信号量
sem_init(sem_t *sem,int pshared,unsigned int value);
0-线程同步1-进程同步
value-最多有几个线程操作共享数据
销毁信号量
sem_destroy(sem_t *sem);
加锁
sem_wait(sem_t *sem);
调用一次相当于对sem做了一次 -- 操作
如果sem值为0,线程会阻塞
尝试加锁
sem_trywait(sem_t *sem);
sem == 0;加锁失败,不阻塞,直接发牛
限时尝试加锁
sem_timewait(sem_t *sem,xxxx);
解锁++
sem_post(sem_t *sem);
对sem做了++ 操作
4.练习
使用信号量实现生产者,消费者模型