二、多任务并发

单任务:程序运行起来,只能处理一件事情

多任务:程序运行起来,可以同时处理多件事情

2.线程
6.线程相关接口

pthread_exit();

void pthread_exit(void *retval);
功能:结束一个线程
参数:
	retval :可以用来保存线程退出时传递的参数
			和return返回的指针类似
注意:
线程退出:
	1. 在线程任务中return
	2. 在线程任务中pthread_exit
	3. 在线程中exit,让所在进程直接退出

phtread_self();(得到线程的pid)

pthread_join();

int pthread_join(pthread_t thread, void **retval);
功能:等待回收具有非分离属性的线程
参数:
	thread:要回收的线程ID;
	retval:接收回收的线程退出时传递的参数:通过pthread_exit参数,return返回值传递
			NULL : 不接收参数
返回值:
	成功:0
	失败:非0
7. 线程的分离属性

分离属性 :不需要被其他线程回收的线程,称为具有分离属性的线程,结束时可以被操作系统回收。

非分离属性:可以被其他线程回收或者结束的线程称为非分离属性的线程

​ 默认属性:非分离属性

设置线程分离属性:

int pthread_detach(pthread_t thread);
功能:设置线程的分离属性
参数:
	thread:要设置的线程的线程ID
返回值:
	成功:0
	失败:!0
8. 线程的互斥机制

多线程访问临界资源时,存在资源竞争问题。

临界资源:多个线程可以同时操作的资源,比如:全局变量,共享内存等。

如何解决资源竞争问题:

线程的互斥机制:让多个线程在访问临界资源时,具有排他性访问的特性

互斥机制:互斥锁、信号量、读写锁(写独占,读共享)、自旋锁

互斥锁:实现互斥机制

        创建互斥锁:pthread_mutex_t

        初始化锁:pthread_mutex_init

        加锁:pthread_mutex_lock/pthread_mutex_trylock

        解锁:pthread_mutex_unlock

        销毁锁:pthread_mutex_destroy

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
           const pthread_mutexattr_t *restrict attr);
功能:初始化互斥锁
参数:
	mutex :互斥锁对象地址
	attr: 锁的属性
返回值:
	成功:0
	失败:非0
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:以阻塞方式等待加锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);
功能:以非阻塞方式加锁,如果该锁已被使用,则返回
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:解锁/释放锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:销毁锁
9. 线程间同步机制

线程间同步:让多个线程任务执行某部分任务的时候具有先后顺序。

同步方式:信号量

可以将信号量看成一个资源数,当该资源数>0的时候,可以申请到信号量,当资源时为0时,申请信号量阻塞等待。申请完信号量时,该信号量对应的资源数自动-1。释放对应的信号量时,该信号量对应的资源数自动+1。

1.创建信号量:sem_t

2.初始化信号量:

int sem_init(sem_t *sem, int pshared, unsigned int value);

功能:初始化信号量

参数:

​         sem:初始化的信号量对象地址

​         pshared:进程间共享还是线程间

​                 0: 线程间

                ​ 非0:进程间

                ​ value:初始化的资源数

返回值:

​         成功:0

​         失败:-1

3.申请信号量(P操作): int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem);

4.释放信号量(V操作) int sem_post(sem_t *sem);

5.销毁信号量 int sem_destroy(sem_t *sem);

10.死锁

死锁:死锁指的是在多线程环境中,每个执行流(线程)都有未释放的资源,且互相请求对方未释放资源,从而导致陷入永久等待状态的情况。
    现象:
    现象1:忘记释放锁
    现象2:重复加锁
    现象3:多线程多锁,抢占锁资源不当
    如:线程A获取了1锁,线程B获取了2锁,同时线程A还想获取2锁,线程B还想获取1锁


    *产生死锁的四个必要条件(四条全中 = 死锁,破掉任意一条 = 解除):
    (1) 互斥条件:一个资源同一时刻只能被一个任务占用(一个执行流获取锁后,其它执行流不

        能再获取该锁)。


    (2) 请求与保持条件:已经拿着一部分资源不释放,同时又去申请新的资源(执行流本身使用

        着一把锁并不释放,还在请求别的锁)。


    (3) 不剥夺条件:资源只能由持有者主动释放,系统/其他任务不能强行抢过来(A执行流拿着

        锁,其它执行流不能释放)。


    (4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系(多个执行流拿着对

        方想要的锁,并且各执行流还去请求对方的锁)。

    解决方法:
    1.锁一定要成对出现
    2.使线程的加解锁顺序一致
    3.破坏环路等待条件
      使用非阻塞锁,一旦线程发现请求的锁被使用,就去释放自己拥有的资源

3.进程间通信机制(IPC)

进程间空间独立,不能直接通信,数据共享需要使用IPC机制

进程间通信的方法:

同一主机间进程通信:

1.有名管道

2.无名管道

3.信号

4.共享内存

5.消息队列

6.信号量集

不同主机间进程通信:

1.网络套接字

1.无名管道

无名管道:只能用在具有亲缘关系的进程间通信

有名管道:可以用在任意进程间通信

int pipe(int pipefd[2]);
功能:创建一个无名管道并获得两个文件描述符
参数:
	pipefd:保存读写端对应的文件描述符
		pipefd[0] --->读端
		pipefd[1] --->写端
返回值:
	成功:0
	失败:-1
读管道:read();
写管道:write();
关闭管道:close();

注意:

  1. 管道在使用时要确定它的方向,是一个单向的数据通道

  2. 管道中的数据,一旦被读走,直接剪切走数据

  3. 管道中的数据遵循先进先出的特点(FIFO)、

  4. 管道默认大小65536字节,64k。

  5. 管道的读端和写端不能交换。

管道的特点:

  1. 读阻塞:当读写端都存在时,从管道中读数据,管道为空时,发生读阻塞

  2. 写阻塞:当管道读写段都存在时,向管道中写数据,管道满时,发生写阻塞

  3. 管道破裂:当读端关闭,向管道中写入数据时,发生管道破裂(异常)

  4. 读返回0:当写端关闭,读管道时,管道中有数据则读出数据,没有数据,则读不发生阻塞,直接返回0

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐