线程间同步机制:让多个线程在执行某个任务时具有先后顺序的执行;
异步:多线程、多进程本质是异步执行;
让多线程在执行时具有先后顺序
信号量:实现线程间同步;
操作步骤:
1.定义信号量对象:sem_t;
2.初始化信号量;
3.PV操作(申请p与释放v信号量);
申请:sem_wait(&sem_w);
释放:sem_post(&sem_r);
4.销毁信号量:sem_destroy(&);
int sem_init(sem_t *sem,int pshared, unsigned int value)
#include<semaphore.h>
功能:初始化信号量;
参数:
sem:要初始化信号量的地址;
pshared:
0:线程间共享;
!0:进程间共享;
value:信号量的初始值(初始资源数)
返回值:
成功:0;
失败:-1
例(使用三个线程按顺序打印A,B,C):
#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
#include<unistd.h>
char buff[3] = {'A','B','C'};
sem_t sem1;
sem_t sem2;
sem_t sem3;
void *task1(void *arg)
{
while(1)
{
sem_wait(&sem1);
printf("task1 = %c\n",buff[0]);
sleep(1);
sem_post(&sem2);
}
}
void *task2(void *arg)
{
while(1)
{
sem_wait(&sem2);
printf("task2 = %c\n",buff[1]);
sleep(1);
sem_post(&sem3);
}
}
void *task3(void *arg)
{
while(1)
{
sem_wait(&sem3);
printf("task3 = %c\n",buff[2]);
sleep(1);
sem_post(&sem1);
}
}
int main(void)
{
pthread_t tid[3];
sem_init(&sem1,0,1);
sem_init(&sem2,0,0);
sem_init(&sem3,0,0);
pthread_create(&tid[0],NULL,task1,NULL);
pthread_create(&tid[1],NULL,task2,NULL);
pthread_create(&tid[2],NULL,task3,NULL);
pthread_join(tid[0],NULL);
pthread_join(tid[1],NULL);
pthread_join(tid[2],NULL);
sem_destroy(&sem1);
sem_destroy(&sem2);
sem_destroy(&sem3);
return 0;
}
互斥锁、信号量、读写锁、自旋锁------>锁
死锁:死锁指的是在多线程环境中,每个执行流(线程)都有未被释放的资源,且互相请求对方未释放资源,从而导致陷入永久等待的情况;
现象:
现象1:忘记释放锁;
现象2:重复加锁;
现象3:多线程加锁,抢占资源不当;
产生死锁的四个必要条件:
(1)互斥条件:一个资源每次只能被一个进程使用;
(2)请求与保持条件:进程因请求资源而阻塞时,对已获得的资源保持不放;
(3)不可剥夺条件:进程以获得的资源,在未使用完之前,不能强行剥夺;
(4)循环等待条件:若干进程之间形成一种头尾相连的循环等待资源关系;
解决办法:
(1)锁一定要成对出现;
(2)使线程的加锁顺序一致;
(3)破坏环路等待条件;
非阻塞锁 pthread_mutex_trylock()
多任务并发:
1.多进程;
2.多线程;
3.IPC机制(进程间通信的通信机制)
原因:因为进程间空间是独立的,不能直接通信;
同一主机进程间通信:
1.古老的通信方式
无名管道;
有名管道;
信号:进程间通知机制;
2.IPC对象通信 system v
共享内存(效率最高);
消息队列;
信号量集(信号灯);
主要在不同主机进程间通信
3.socket通信
网络通信
IPC机制:
1.管道
有名管道:可以用于同一主机,任意进程间通信;
无名管道:只能用于同一主机,具有亲缘关系的进程(父子进程);
2.无名管道
每个进程的内核是共用的,另外的0-3G是独立的,无名管道在内核中创立一个空间,其他进程能够对该空间进行读写操作(无名);
(1)无名管道操作流程
<1>创建无名管道:pipe(); 成功:0
pipefd[0] ----->读端;
pipefd[1]------>写端;
<2>写管道---->write();
<3>写管道---->read();
<4>关闭管道---->close();
void *memset(void *s,int c,size_t n);
功能:将内存区域填充成指定的数据;
(2)管道本质:
内核空间中的一段缓冲区,遵顼先入先出特点
无名管道的:读端:pipefd[0];
写端:pipefd[1];
二者不能交换
(3)管道的特性:
(1) 写阻塞:读端与写端都存在,向管道中写数据,当管道满时,发生写阻塞;
(2)读阻塞:读端与写端都存在,向管道中读数据,当管道为空,发生读阻塞;
(3)读返回0:当写端关闭,从管道中读数据,若管道中有数据,则读到数据;若管道中没有数据,返回0,不再阻塞;
(4)管道破裂:读端关闭,向管道中写入数据,发生管道破裂(异常)
通信方式:v
单工:广播;
半双工:对讲机
全双工:电话
3.有名管道
与无名管道类似,不过内核中管道开辟的空间(缓冲区)与文件(管道文件)相关联。
有名管道的操作流程:
1.创建一个管道文件:mkfifo、mkfifo():
2.打开管道文件:open;
3.读/写管道文件:read、write;
4.关闭管道文件close;
int mkfifo(const char *pathname,mode_t mode)
功能:创建一个管道;
参数:
pathename:文件名;
mode:管道文件的读写执行权限;
返回值:
成功:0;
失败:-1;
4.信号
信号:实现进程间的通知机制
实现进程间的异步通信
软中断
异步通信:接收方不知道什么时候发送方什么时候发送数据;
(1)系统支持的信号有哪些
kill -l 查看
2)SIGUNT: CTRL + C
打断进程
3)SIGQUIT ctrl + \
使进程结束
9)SIGKILL
杀死进程(管理员信号结束90%进程)
10)SIGUSR1
用户自定义的信号
11)SIGSIGV
进程强制中止(段错误信号)
12)SIGUSR2
用户自定义的信号
13)SIGPIPE
管道破裂时发送出来的信号
14)SIGALRM
让一个进程结束(定时到达)
17)SIGCHLD
子进程要结束发给父进程(自动)
18)SIGCONT
进程从暂停态继续运行
19)SIGSTOP
进程进入暂停态(管理员信号)
20)SIGTSTP
ctrl + Z让进程进入暂停态,变为后台进程
(2)信号的处理流程
<1> 缺省(xing):按照默认处理方式处理;
<2> 忽略:不处理;
<3> 捕获:以自定义方式处理;
signal:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:设置处理信号的方式(注册一个信号)
参数:
signum:要处理的信号的编号;
handler:
SIG_IGN:以忽略方式处理该信号(不处理);
SIG_DFL:以缺省方式处理(默认);
函数的地址:以捕获的方式处理(自定义);
返回值:
失败:NULL;
自定义方式时:返回自定义的函数入口;
void(*sighandler_t)(int signum)
sighandler_t :
注意:
1.若信号不被注册,则按照默认方式处理;
2.信号只需注册一次即可;
3.每次信号的到来都会触发一次任务处理函数;
4.信号需要早注册;