linux从线程创建到线程安全详细分析

线程控制:创建/终止/等待/分离

操作系统没有线程的控制接口,现在我们看到的函数是封装要的库函数

创建:int pthread_create(pthread_t *tid,pthread_attr_t *attr,void*(*thread_routine)(void *),void *arg)

每个线程在进程的虚拟地址空间中都有一个自己相对独立的线程地址空间(主要是线程的栈存储)

第一个参数:新线程ID,创建成功系统返回。

第二个参数:新线程的属性,NULL为默认属性。

第三个参数:新线程的启动函数 函数指针: 指向形参位void* 返回值为void*类型的函数的指针。

void* thread_fun(void)

第四个参数:传递给新线程。

终止:如何退出线程

线程入口函数调用return,线程入口函数运行完毕就会退出线程(main函数中return退出的是进程)

在任意位置调用pthread_ext(void *retval)

在其它线程中调用pthread_cancel(pthread_t tid);

等待:等待线程退出,获取线程退出返回值,释放线程资源

线程有一个默认属性:-joinable,这种属性的线程退出后不会自动释放资源,需要被其它线程等待。

pthread——join(pthread——t tid,void **retval)

分离:线程属性从joinable设置为detach。

线程属性为detach的线程,退出后会自动释放资源,不需要被等待!

pthread_detach(pthread_t tid)

线程安全:多个执行流对临界资源的争夺访问,但是不会出现数据二异性

线程安全的实现:

同步 :通过条件判断对临界资源访问的合理性!

互斥:通过同一时间对临界资源访问的唯一性实现临界资源访问的安全性

互斥的实现:互斥锁

互斥锁实现互斥的原理:互斥锁本事而言是一个0/1计数器,描述了一个临界资源的当前访问状态,所有执行流访问当前临界资源都要进行判断当前临界资源状态是否允许访问,如果不允许则执行流进行等待,否则便让执行流访问临界资源,但在访问时应该讲临界资源状态改为不可访问状态,这期间不允许别的执行流访问临界资源!

互斥锁具体的操作流程以及接口介绍:

1.定义互斥锁变量:pthread_mutex mutex;

2.初始化互斥锁变量

pthread_mutex init(pthread_mutex_t *mutex,pthread_mutexattr_t *attr)

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER 这是一个宏,里面存储了一个结构体的值

3.在访问临界资源之前进行加锁操作(不能加锁则等待,可以加锁则修改资源状态,然后调用返回,访问临界资源)

pthread_mutex_lock(pthread_mutex_t *mutex)/pthread_mutex_trylock(pthread_mutex_t *mutex);加锁后在任意有可能 退出线程的地方都要解锁!

挂起等待状态:将线程状态置为可中断休眠状态----表示当前休眠;被唤醒 :将线程状态置为运行状态

4.在临界资源访问完毕之后进行解锁操作(将资源状态 置为可访问,将其他执行流唤醒)

pthread_mutex_unlock(pthread_mutex_t *mutex);

5.销毁互斥锁

pthread_mutex_destroy(pthread_mutex_t *mytex)

所有的执行流都需要通过同一个互斥锁实现互斥,意味着互斥锁本身就是一个 临界资源,大家都会访问。如果互斥锁本身的操作读不安全如何保证别人安全?

互斥锁本身的操作首先必须是安全的------互斥锁自身计数的操作是原子操作

CPU                                                                                                  数据交换                                                                                                                        内存

                                                                                                          寄存器的值与内存的值进行交换一步完成 

寄存器(先将寄存器值置0,跟内存中数据直接进行调换,一步完成)                                                                                                                      mutex(计数器变量)

不管mutex的状态是什么,反正一步交换之后,其他的线程都是不可访问的;这时候当前线程就可以慢慢判断了(起初是怕mutex在上传后,有访问互斥锁的线程出现)

1.先将寄存器的值置为0

2.直接将寄存器的值与内存空间中的数据进行交换-------这个指令是一步可以完成的(这个时候内训中mutex的值就是0,别人访问肯定发现无法加锁)

3.判断寄存器中的值是否为1

if(%eax==1)

如果是1,则pthread_mutex_lock直接返回,访问临界资源

else

如果是0,则让线程等待!

所有CPU处理指令,都需要将数据从内存拿到CPU寄存器上面来,在CPU寄存器上进行处理。处理完毕之后再返回到内存!

死锁:多个执行流对锁资源进行争抢访问,但是因为访问推进顺序不当,造成互相等待最终导致程序无法继续推进,这时候就造成了死锁。死锁实际上是一种程序无法继续推进,卡在某一位置的一种概念。死锁产生通常是在访问多个锁的时候需要注意的事项!

死锁产生的必要条件:必须具备的条件,有一条不满足就不会产生死锁

1.互斥条件:我加了锁,别人就不能继续加锁!

2.不可剥夺条件:我加的锁,别人不能解,只有我能解锁

3.请求与保持条件:我加了A锁,然后去请求B锁;如果不能对B锁加锁,则也不释放A锁

4.环路与等待条件:我加了A锁,然后去请求B锁;另一个人加了B锁,而后去请求A锁!

死锁的预防:破坏死锁产生的必要条件!主要避免3,4号条件

死锁的避免:死锁检测算法/银行家算法

银行家算法基本阐述:一张表记录当前有哪些锁    一张表记录已经给谁分配了指定的锁     一张表记录谁当前需要哪些锁

按照三张表进行判断,判断若给了一个执行流分配了指定的锁,是够会达成环路等待条件导致系统的运行进入不安全状态,如果有可能就不能分配。反之,若分配了之后不会造成环路等待,

则系统是安全的,则分配这个锁------破坏环路等待条件

后续若不能 分配锁,可以资源回溯,把当前执行流中已经加的锁释放掉--------破坏请求与保持

非阻塞加锁操作,若不能够加锁,则把手上的其他锁也释放掉-----------------破坏请求与保持

加锁是对临界资源进行保护,实际上是对程序性能的一种极大挑战

高性能程序中通常会研究一种无锁编程-------CAS锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值