一、线程间通信
1.实现方式
一个进程空间内部的所有线程共享数据段和堆区,所以全局变量、静态变量、堆区空间都是共享的,可以利用这些空间通信
多线程操作全局变量空间时会引入资源竞争
多线程要避免引入资源竞争可以通过加互斥锁解决
线程间通信最简单的方法:全局变量 + 锁
2.原子操作
不会被CPU任务调度打断的一次最小的操作
3.互斥锁
1.避免多线程资源竞争,配合资源使用,使用资源前加锁,使用资源结束后解锁
2.加锁后,无法再次加锁,必须等到解锁后才能继续加锁
4.临界代码与临界区
加锁解锁中间的代码称为临界代码或临界区
临界代码或临界区不可能同时被CPU任务执行
5.互斥锁函数接口
1.pthread_mutex_init
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
功能:
互斥锁的初始化
参数:
mutex:互斥锁空间首地址
attr:互斥锁的属性,默认传NULL
返回值:
成功返回0
失败返回非0
2.pthread_mutex_destory
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:
互斥锁的销毁
参数:
mutex:互斥锁空间首地址
返回值:
成功返回0
失败返回非0
3.pthread_mutex_lock
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:
互斥锁加锁
参数:
mutex:互斥锁空间首地址
返回值:
成功返回0
失败返回非0
4.pthread_mutex_unlock
int pthread_mutex_unlock(pthread_mutex_t *mutex)
功能:互斥锁解锁
参数:
mutex:互斥锁空间首地址
返回值:
成功返回0
失败返回非0
6.死锁
多任务通信过程中由于枷锁导致多个人物均无法向下执行的状态
1.死锁产生的必要条件
互斥条件
不可剥夺条件
请求保持
循环等待
2.解决死锁
1>用pthread_mutex_tylock 替代 pthread_mutex_lock
2>加锁顺序保持一致
7.信号量
同步所有线程,让多个任务执行有先后顺序
1)同步:拥有严格的先后执行的逻辑顺序关系
2)异步:代码执行流程没有任何关联
3)信号量是一个资源,资源可以初始化、销毁、申请、释放
- 若资源数为0,申请资源时会阻塞等待,等待释放资源后,才能拿到资源
- 若资源数 >0 ,则申请资源,资源数-1
- 释放不会阻塞, 资源数+1
8.信号量函数接口
1. sem_init
int set_init (set_t *sem, int pshared, unsigned int value);
功能:
初始化信号量
参数:
- sem: 信号空间首地址
- pshared: 信号的作用域
0, 线程间
非0, 进程间
- valu:信号量的初始值
返回值:
成功返回0; 失败返回非0值
2.sem_destroy
int sem_destroy(sem_t *sem)
功能:
初始化信号量
参数:
- sem: 信号空间首地址
- pshared: 信号的作用域
0, 线程间
非0, 进程间
- valu:信号量的初始值
返回值:
成功返回0; 失败返回非0值
3.sem_wait
int sem_wait (sem_t *sem)
功能:
申请资源,让资源数-1, 若资源为 0 ,则阻塞等待
参数:
- sem: 信号空间首地址
返回值:
成功返回0; 失败返回非0值
4.sem_post
int sem_post (sem_t *sem)
功能:
释放资源,让资源数+1
参数:
sem:信号量空间的首地址
返回值:
成功返回0
失败返回非0
练习:创建3个线程任务,线程任务分别打印A B C
-
线程1循环打印A
-
线程2循环打印B
-
线程3循环打印C
要求三个线程打印出来的顺序总为A B C
cpp
#include<stdio.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>
char tmp[4096] = {0};
sem_t w;
sem_t r;
sem_t p;
void* thread1 (void *arg)
{
while(1)
{
sem_wait(&w);
printf("A");
sem_post(&p);
}
return NULL;
}
void* thread2 (void *arg)
{
while(1)
{
sem_wait(&p);
printf("B");
sem_post(&r);
}
return NULL;
}
void* thread3 (void *arg)
{
while(1)
{
sem_wait(&r);
printf("C\n");
sem_post(&w);
}
return NULL;
}
int main(void)
{
pthread_t tid1;
pthread_t tid2;
pthread_t tid3;
sem_init(&w, 0, 1);
sem_init(&r, 0, 0);
sem_init(&p, 0, 0);
pthread_create(&tid1, NULL, thread1, NULL);
pthread_create(&tid2, NULL, thread2, NULL);
pthread_create(&tid3, NULL, thread3, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_join(tid3, NULL);
sem_destroy(&r);
sem_destroy(&w);
sem_destroy(&p);
return 0;
}
二、进程间通信
1.进程间通信的方式
通过内核通信
1.管道(最简单、最方便)
2.信号
3.消息队列
4.共享内存(最高效)
5.信号灯
6.本地域套接字
2.管道
1.无名管道
- 只能用于具有亲缘关系的进程间通信
2.有名管道
- 任意进程间的通信
3.函数接口
1.pipe
int pipe(int pipefd[2]);
功能:
创建一个用于进程间通信的内核缓存区, 返回两个用于读写该内核空间的文件描述符
参数:
pipefd:数组
pipefd[0] : 读文件描述符
pipefd[1] : 写文件描述符
返回值:
成功返回0
失败返回-1
