Linux多任务-线程篇

一.线程

线程最直接的理解就是轻量级进程,它是一个基本的CPU执行单元,也是程序执行流的最小单元,由线程ID、程序计数器、寄存器集合和堆栈组成。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。

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

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

二.线程的创建

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

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

三.进程与线程的区别

进程

线程

概念

操作系统资源分配的最小单位

CPU任务调度的最小单位

效率

操作系统要为进程分配0-4G虚拟内存空间

线程创建比进程快,只需要分配栈区;线程间任务切换速度快,系统开销小

内存

内存消耗大,相同资源环境下,并发量比线程小

只需要独立的栈区空间,其他空间和进程共享

安全性

各进程空间独立,安全性高

公用空间,没有进程安全性高

通信

进程不能直接通信,需要使用IPC机制通信

线程间可以直接通信:全局变量、全局队列

四.线程的调度

宏观并行,微观串行,调度方式与进程一样

五.线程消亡

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)

回收方式:

  • pthread_join()

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

功能:阻塞回收线程资源空间和状态,直到被指定的子线程执行完毕并返回(或调用了 pthread_exit),它才会继续执行后面的代码。

参数:

        thread:要回收的线程的ID

        retval:保存线程状态的变量地址(接收pthread_exit()的返回值)(类似wait与exit)

                       NULL:表示不接收状态

返回值:

        成功:0

        失败:非0

注意:
1. 使用线程相关接口时,编译和链接需要链接线程库:-lpthread/-pthread。
2. 创建线程的主进程不能先于线程结束。

  • 线程退出及资源回收

1.线程执行结束,return

2.线程所在进程结束(exit)

3.pthread_exit(NULL)退出线程

七.线程的非分离属性

线程的分离属性:不需要被其他线程回收的线程,称为具有分离属性的线程,将来执行结束,由操作系统回收,类似孤儿进程(区别在父进程/线程是否改变,前者是在创建线程时就被贴上了分离标签,线程结束操作系统自动回收;后者是父进程先于子进程结束,子进程被init(PID 1)收养,由 init 代为执行 wait。)

线程的非分离属性:可以被其他线程结束和回收的线程,称为具有非分离属性的线程,类似僵尸进程。由父线程/进程主动调用 pthread_join(线程)或 wait(进程),系统才会彻底清理)。所以,具有非分离属性的线程结束后,必须要被其他线程回收。

pthread_create创建的线程默认为非分离属性

  • pthread_detach(tid)

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

场景:

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

八.线程的同步和互斥

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

  1. 线程间互斥机制

多线程访问临界资源时,存在资源竞争问题。
临界区:访问临界资源的这段代码称为临界区
临界资源:多个线程可以同时访问的区域。如:全局变量,全局队列、文件、数据库

线程的互斥机制:让多个线程访问临界资源时,具有排他性访问的特性。
互斥机制:互斥锁,锁被占用时,线程会阻塞,让出CPU,等锁释放后被内核唤醒。
1.创建一个互斥锁对象:pthread_mutex_t
2.初始化互斥锁:pthread_mutex_init();
3.加锁:pthread_mute_lock();

4.解锁:pthread_mutex_unlock();
5.销毁互斥锁:pthread_mutex_destroy()。

pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);
pthread_mutex_destroy(&mutex);

int pthread_mutex_init(pthread_mutex_t *restrict mutex,

                                        const pthread_mutexattr_t *restrict attr);
功能:初始化一个锁对象

参数:
        mutex:要初始化的锁对象的地址
        attr:锁属性的变量地址(NULL:按照默认属性创建》
返回值:
        成功:0
        失败:!0

其他锁:

        读写锁:读共享,写独占

        自旋锁:锁被占用时,线程会“原地打转”(忙等待,死循环不断检查锁是否被释放),CPU被它一直占着,直到拿到锁。

  • 2.线程间同步机制

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

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

1.创建信号量对象:sem_t sem;
2.初始化信号量:sem_init(&sem ,0,3);//0表示线程,3表示初始值
3.申请信号量(P操作):sem_wait(&sem);
4.释放信号量(V操作):sem_post(&sem)
5.销毁信号量:sem_destroy(&sem);

int seminit(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.破坏环路等待条件
使用非阻塞锁,一旦线程发现请求的锁被使用,就去释放自己拥有的资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值