线程锁与其他用法
Posted fuzhang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程锁与其他用法相关的知识,希望对你有一定的参考价值。
一、死锁与递归锁
死锁的现象归根在于函数功能中含有连续锁,并且,各个锁的加锁与解锁时存在一定的时间差。当多个此类函数出现加锁的次序不一致时,当不同进程/线程调用这些函数,可能因为首次抢得锁的进程/线程释放锁的次序不同,导致其他进程/线程抢得锁的样式也不一致,无法在一个功能函数中连续使用,出现死锁。
优点:针对性加锁,每把锁赋予不同的权限,便于把控函数进程。
缺点:不同功能函数可能因为功能逻辑需要,加锁次序不同,但是在线程在调用多个函数时,就会可能出现死锁现象,注意点较多,无法完成对应加锁解锁,降低的使用效率与准确度。
递归锁:
为了解决死锁现象,引入了递归锁。
递归锁符号: from threading import RLock
lock_A=lock_B=RLock()
大致原理是,初始化时,将所有的锁同等看待。不计锁的类型只计加锁解锁的次数。初始化时,默认加锁次数为0,函数每经过一次加锁,计数增加1,每解一次锁,技术减去1。当加锁次数回归到0时,说明函数处于可被多个进程/线程争抢调用阶段。
优势:将所有的锁的一视同仁,不必对应加解同类型的锁,只需考虑加锁次数。在进程/线程调用函数时,方便检测与使用。
缺点:但是锁的针对性较差。无法有针对性的加锁。
二、GIL全局锁
因为同一线程总的数据是共享的,为了保证数据的安全性与稳定,python在早期开发的时候,因为当时的CPU只有一核,便针对全线程加锁,此锁成为GIL全局锁.随着后续科技发展,出现多核处理器,但是python全局锁保留下来(由于更改语言的耗费太大),由此导致了影响下面的处理数据的差异。这种差异针对cpython(python为解释性语言,即地层基于C语言识别字节码的解释器)较为明显,其他的jpython与pypy等等不受此影响。
cpython解释路径:
大致路径: 文件——编译器(c语言识别的字节码)——虚拟机(字节码转化成机器码)——CPU。
由此cpython限定,同一时间单核只能允许一个线程/进程解释器。
加全局锁的优点:保证了cpython解释器的资源安全性。
缺点:单个进程的多线程不能利用多核资源,理论上降低了CPU的使用效率。
cpython运行不同类型阻塞出现了自身的特点:
1、I/O阻塞
单个进程的多个线程并发的运行速度并不比多进程并发慢。
原因在与单核CPu在同进程的多线程运行遇到IO阻塞,便会在多线程之间进行切换。而多个CPU运行多进程(并发)每个进程遇到IO阻塞,处于等待状态,同样会相互切换,设想当全部进程处于阻塞状态时,所有的CPU同样均会处于停滞状态。另外,线程的开启速度要快于进程,线程的开启耗费资源要低于进程。由以上两点,决定单进程的线程(单核)并行与多进程并发(多核)相比,稍具优势。可以简单理解为,阻塞来自外部,再好的计算机同样需要等待外部处理。
开启多线程运行速度要高于多进程。
2、计算密集型阻塞
计算密集型阻塞出现的原因在于CPU运算并未出现代码源运行终断,只是在与CPU运行时间过长,会在多个计算过程中进行切换。
在此过程中,单进程中多线程并行,与多进程并发的区别在于单核CPU运算要在不同线程之间来会切换,就是一个CPU同时处理多个运算,资源要在多个线程之间分配。而针对多核CPU,每个CPU处理单个运算,并不用相互切换,此时计算机合理分配了CPU资源,CPU会专注进行单个运算。时间较短。
3、GIL全局锁与lock锁的区别
相同点:都是同种锁,互斥锁
不同点:
1)GIL全局锁,保护解释器内部资源数据安全。
2)GIL上锁与解锁无需手动。
3)自己代码中定义的互斥锁保护进程中的资源数据安全。
4)自己定义的互斥锁,需要自己手动加锁解锁。
三、信息量
信息量允许多个线程/进程共抢CPU资源。
符号及使用: from threading import Semaphore,用法与lock相似。
首先初始化,规定单次最大处理量 sem=Semaphore(5).
其次在被执行函数内部语句加锁,sem.acquire()
最后,在执行语句之后,解锁sem.release()。
大致作用是,规定最大同时可以运行的线程数量,假如需要处理的线程数量较多,就会类似银行业务窗口实现空位一次填补。
四、基于多线程的socket通信
基于多线程的socket与之前多链接(服务器双重while循环)基于socket通信的异同:
前者主要要解决一个如何在同一server端下开启多线程的问题。即分清主线程的完成的任务与子线程完成的任务。
服务端:
主线程的任务要完成,初始化,绑定地址(服务器只能绑定一个地址),完成循环监听,响应连接并开启子线程的任务,其中,作为服务器,开启子进程的数量越大越好,任务较为关键。(若要限定,可以考虑for循环,若无限定,采用while循环)
子线程主要完成循环收发消息,实现通信等任务,即实现线程运行中调用函数。调用函数根据函数需求,确定传参的数量与类型。
客户端:与普通socket通信功能没有太大区别。
以上是关于线程锁与其他用法的主要内容,如果未能解决你的问题,请参考以下文章