一、线程

线程是一个轻量级的进程,用来实现多任务并发。

线程是操作系统任务调度的最小单位。

进程是操作系统资源分配的最小单位。

二、线程的创建

线程由所在进程创建,线程一定是属于某个进程的。

线程创建时,所在进程为其分配独立的栈区空间(默认8M),线程共用所在进程的堆区、数据区、文本区、内核区域。

三、进程与线程的区别

四、线程的调度

宏观并行,微观串行

(调度方式与进程一样)

五、线程消亡

1. 线程退出

2. 回收线程资源空间

六、线程相关函数接口

创建线程:

pthread_create()

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,

                                void *(*start_routine) (void *), void *arg);

功能:创建并启动一个线程

参数:

        thread :保存线程ID的变量地址

        attr :线程属性的变量地址

        NULL :按照默认属性创建

        start_routine:函数指针:指向线程执行的任务函数的指针(线程任务函数)

        arg : 传递给线程任务函数的参数

返回值:

        成功:0

        失败:错误码(!0

注意:

1. 使用线程相关接口时,编译和链接需要链接线程库:-lpthread/-pthread

2. 创建线程的主进程不能先于线程结束。

线程退出及资源回收:

        1. 线程执行结束,return

        2. 线程所在进程结束

        3. pthread_exit退出线程

七、线程的非分离属性

线程的分离属性:不需要被其他线程回收的线程,称为具有分离属性的线程,将来执行结束,由操作系统回收。

        设置tid对应的线程为分离属性的线程。

        pthread_detach(tid);

        场景:

                1. 其他线程没有机会回收该线程时,可以将该线程设置成分离属性。

线程的非分离属性:可以被其他线程结束和回收的线程,称为具有非分离属性的线程。

                                所以,具有非分离属性的线程结束后,必须要被其他线程回收。

                                默认属性:非分离属性

回收方式:pthread_join()

int pthread_join(pthread_t thread, void **retval);

功能:阻塞回收线程资源空间和状态

参数:

        thread:要回收的线程的线程ID

        retval :保存线程状态的变量的地址

                NULL :表示不接收状态

返回值:

        成功:0

        失败:!0

八、线程的同步和互斥

线程间通信:全局变量、队列、pthread_create传参、条件变量

线程间互斥机制

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

临界资源:多个线程可以同时访问的区域。如:全局变量,全局队列、文件、数据库

临界区:访问临界资源的这段代码称为临界区

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

互斥机制:互斥锁

1. 创建一个互斥锁对象:pthread_mutex_t

2. 初始化互斥锁 :pthread_mutex_init();

3. 加锁 :pthread_mutex_lock();

4. 解锁 : pthread_mutex_unlock();

5. 销毁互斥锁:pthread_mutex_destroy();

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                        const pthread_mutexattr_t *restrict attr);
功能:初始化一个锁对象
参数:
    mutex:要初始化的锁对象的地址
    attr :锁属性的变量地址(NULL:按照默认属性创建)
返回值:
    成功:0
    失败:!0

其他锁:

读写锁:读共享,写独占

自旋锁:申请不到锁时,一直占用CPU

线程间同步机制

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

信号量:实现线程间同步方法

1. 创建信号量对象 : sem_t

2. 初始化信号量 : sem_init();

3. 申请信号量(P操作): sem_wait();

4. 释放信号量(V操作): sem_post()

5. 销毁信号量 : sem_destroy();

int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:初始化一个信号量
参数:
     sem:信号量对象地址
     pshared:
             0 : 线程间共享使用
             !0:进程间共享使用
             value:信号量初始值
返回值:
     成功:0
     失败:!0

九、死锁

死锁:死锁指的是在多线程环境中,每个执行流(线程)都有未释放的资源,且互相请求对方未释放资源,从而导致陷入永久等待状态的情况。

现象:

现象1:忘记释放锁

现象2:重复加锁

现象3:多线程多锁,抢占锁资源不当

如:线程A获取了1锁,线程B获取了2锁,同时线程A还想获取2锁,线程B还想获取1锁。

产生死锁的四个必要条件(四条全中 = 死锁,破掉任意一条 = 解除):

1) 互斥条件:一个资源同一时刻只能被一个任务占用(一个执行流获取锁后,其它执行流不能再获取该锁)。

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

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

4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系(多个执行流拿着对方想要的锁,并且各执行流还去请求对方的锁)。

解决方法:

1.锁一定要成对出现

2.使线程的加解锁顺序一致

3.破坏环路等待条件

        使用非阻塞锁,一旦线程发现请求的锁被使用,就去释放自己拥有的资源

        int pthread_mutex_trylock(pthread_mutex_t *mutex);

        int sem_trywait(sem_t *sem);

Logo

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

更多推荐