python高级技术(死锁、递归锁、信号量、Event事件、进程池、线程池、协程)
一 死锁和递归锁(了解)
进程也有死锁与递归锁,使用方法类似
所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
当你知道锁的使用抢锁必须要释放锁,其实你在操作锁的时候也极其容易产生死锁现象
(整个程序卡死 阻塞) # 递归锁
递归锁的特点
可以被连续的acquire和release
但是只能被第一个抢到这把锁执行上述操作
它的内部有一个计数器 每acquire一次计数加一 每release一次计数减一
只要计数不为0 那么其他人都无法抢到该锁
from threading import Thread, Lock, RLock
import time mutexA = Lock()
mutexB = Lock()
# mutexA = mutexB = RLock() # 类只要加括号多次 产生的肯定是不同的对象
# 要实现多次加括号是同一对象,要用单例模式 class MyThead(Thread):
def run(self):
self.func1()
self.func2() def func1(self):
mutexA.acquire()
print('{} 抢到A锁'.format(self.name)) # 获取当前线程名
mutexB.acquire()
print('{} 抢到B锁'.format(self.name))
mutexB.release()
mutexA.release() def func2(self):
mutexB.acquire()
print('{} 抢到B锁'.format(self.name))
time.sleep(2)
mutexA.acquire()
print('{} 抢到A锁'.format(self.name)) # 获取当前线程名
mutexA.release()
mutexB.release() if __name__ == '__main__':
for i in range(10):
t = MyThead()
t.start() '''
Thread-1 抢到A锁
Thread-1 抢到B锁
Thread-1 抢到B锁
Thread-2 抢到A锁
'''
上述是死锁现象,加上递归锁:
mutexA = mutexB = RLock()
有计数功能,解决死锁现象
二 信号量(了解)
信号量在不同的阶段可能对应不同的技术点
在并发编程中信号量指的是锁!!!
'''
如果我们将互斥锁比喻成一个厕所的话
那么信号量就相当于多个厕所
'''
from threading import Thread, Semaphore
import time
import random sm = Semaphore(5) # 括号内写数字,写几就是表示开设几个坑位 def task(name):
sm.acquire()
print('{} 正在蹲坑'.format(name))
time.sleep(random.randint(1,5))
sm.release() if __name__ == '__main__':
for i in range(1,21):
t = Thread(target=task, args=('学生{}号'.format(i),))
t.start()
每5个人一组,去抢锁,这一组就开始使用坑位。
三 Event事件(了解)
一些进程/线程需要等待另外一些进程/线程运行完毕之后才能运行,类似于发射信号一样
from threading import Thread, Event
import time event = Event() # 造了一个红绿灯 def light():
print('红灯亮着得')
time.sleep(3)
print('绿灯亮 ')
event.set() def car(name):
print('{} 号车正在等红灯'.format(name))
event.wait()
print('{} 号车加油启动'.format(name)) if __name__ == '__main__':
t = Thread(target=light)
t.start() for i in range(20):
t = Thread(target=car, args=('{}'.format(i),))
t.start()
四 线程q(了解)
import queue # 使用的队列都是只能在本地测试使用 # 1 队列q 先进先出
# 2 后进先出q
q = queue.LifoQueue(3) # last in fist out
q.put(1)
q.put(2)
q.put(3)
print(q.get()) # 优先级q 你可以给放入队列中的数据设置进出的优先级
q = queue.PriorityQueue(3)
q.put((23, '111'))
q.put((0, '222'))
q.put((-2, '333'))
print(q.get()) # put括号内放入一个元组,第一个放数字表示优先级
# 需要注意的是,数字越小优先级越高
五 进程池与线程池(掌握)
先回顾之前TCP服务端实现并发的效果是怎么玩的
每来一个人就开设一个进程或者线程去处理
'''
无论是开设进程还是线程,都要消耗计算机资源
只不过开设线程的消耗比开设进程的小一点
我们是不可能做到无限制的开设进程和线程的,因为计算机硬件的资源跟不上
硬件的开发速度远远赶不上软件
为保证计算机正常工作,不能无限制的开设进程和线程
宗旨是应该在保证计算机硬件能够正常工作的情况下最大限度的利用它
'''
* 池的概念
池是用来保证计算机硬件安全的情况下最大限度的利用计算机
它减低了程序的运行效率但是保证了计算机硬件的安全,从而让你写得程序能够正常运行
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time
import os # pool = ThreadPoolExecutor(5) # 池子里面固定只有五个线程
# 括号内可以传数字 不传的话默认会开设当前计算机CPU个数五倍的线程
pool = ProcessPoolExecutor(5)
# 括号内可以传数字,不传的话默认会开设当前计算机CPU个数进程
'''
池子造出来之后,里面会固定存在五个线程
这五个线程不会出现重复创建和销毁的过程 池子的使用非常的简单
只需要做的任务往池子中提交即可,自动会有人来服务你
''' def task(n):
print(n, os.getpid())
time.sleep(2)
return n ** n def call_back(n):
print('call_back>>>:', n.result()) '''
任务的提交方式
同步:提交任务之后原地等待任务的放回结果,期间不做任何事
异步:提交任务之后不等待任务的返回结果,执行继续往下执行
返回结果如何获取???
异步提交任务的返回结果,应该通过回调机制来获取
回调机制:
相当于每个异步任务绑定了一个定时炸弹
一旦该任务有结果立刻触发爆炸
'''
if __name__ == '__main__': # pool.submit(task, 1) # 朝池子中提交任务 异步提交
# print('主') # t_list = []
for i in range(20):
# res = pool.submit(task, i)
res = pool.submit(task, i).add_done_callback(call_back)
print(res) # None
# print(res.result()) # result方法 同步提交
# t_list.append(res)
# 等待线程池中所有的任务执行完毕之后再继续往下执行
# pool.shutdown() # 关闭线程池 等待线程池中所有的任务运行完毕
# for t in t_list:
# print('--->:', t.result()) '''
程序由并发变成了串行
任务的为什么打印的是None
res.result() 拿到的就是异步提交的任务的返回结果
'''
* 总结
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
pool=ProcessPoolExecutor(5)
pool.submit(task,i).add_done_callback(call_back)
六 协程
进程:资源单位
线程:执行单位
协程:这个概念是程序员自己想出来了,不存在
单线程下实现并发
程序员自己在代码层面上检测我们所有的IO操作
一旦遇到了IO,我们在代码级别完成切换
这样给CPU的感觉是你这个程序一直在运行,没有IO
从而提升程序的运行效率
总结协程特点:
- 必须在只有一个单线程里实现并发
- 修改共享数据不需加锁
- 用户程序里自己保存多个控制流的上下文栈
- 附加:一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制))
多道技术
切换+保存状态
CPU两种切换
1.程序遇到IO
2.程序长时间占用
TCP服务端
accept
recv
切换
切换不一定是提升效率,也可能是降低效率
IO切 提升
没有IO 降低
import time # 串行执行计算密集型的任务 0.5250000953674316
def func1():
for i in range(10000000):
i + 1 def func2():
for i in range(10000000):
i + 1 start_time = time.time()
func1()
func2()
print(time.time() - start_time) # 切换 + yield 0.6111178398132324
import time def func1():
while True:
10000000 + 1
yield def func2():
g = func1()
for i in range(10000000):
i + 1
next(g) start_time = time.time()
func2()
print(time.time() - start_time)
保存状态
保存上一次执行的状态,下一次接到上一次的操作继续往后执行
yield
* 验证切换是否就一定提升效率
# gevent模块(了解)
pip3 install geventfrom gevent import monkey;monkey.patch_all()
import time
from gevent import spawn '''
gevent模块本身无法检测常见的一些IO操作
在使用的时候要额外的加入猴子补丁
''' def func1():
print('he')
time.sleep(2)
print('he') def func2():
print('ha')
time.sleep(3)
print('ha') start_time = time.time()
g1 = spawn(func1)
g2 = spawn(func2)
g1.join() # 等待被检测的任务执行完毕,在往后继续执行
g2.join()
# func1()
# func2()
print(time.time() - start_time) # 3.021846055984497,用时最多的一个,两个来回切换,CPU检测不到有IO操作
'''
he
ha
he
ha
3.0171852111816406
'''
# 协程实现TCP服务端的并发
服务端:
from gevent import monkey
monkey.patch_all()
import socket
from gevent import spawn def communication(conn):
while True:
try:
data = conn.recv(1024)
if len(data) == 0:
break
conn.send(data.upper())
except ConnectionResetError as e:
print(e)
break
conn.close() def server(ip, port):
server = socket.socket()
server.bind((ip, port))
server.listen(5)
while True:
conn, addr = server.accept()
spawn(communication, conn) if __name__ == '__main__':
g1 = spawn(server, '127.0.0.1', 8080)
g1.join()
客户端:
from threading import Thread, current_thread
import socket def x_client():
client = socket.socket()
client.connect(('127.0.0.1', 8080))
n = 0
while True:
msg = '{} say hello {}'.format(current_thread().name, n)
n += 1
client.send(msg.encode('utf-8'))
data = client.recv(1024)
print(data.decode('utf-8')) if __name__ == '__main__':
for i in range(100):
t = Thread(target=x_client)
t.start()
# 总结
理想状态:
我们可以通过
多进程下面开设多线程
多线程下面再开设协程
从而使我们的程序执行效率提升
python高级技术(死锁、递归锁、信号量、Event事件、进程池、线程池、协程)的更多相关文章
- python并发编程-多线程实现服务端并发-GIL全局解释器锁-验证python多线程是否有用-死锁-递归锁-信号量-Event事件-线程结合队列-03
目录 结合多线程实现服务端并发(不用socketserver模块) 服务端代码 客户端代码 CIL全局解释器锁****** 可能被问到的两个判断 与普通互斥锁的区别 验证python的多线程是否有用需 ...
- 并发编程---死锁||递归锁---信号量---Event事件---定时器
死锁 互斥锁:Lock(),互斥锁只能acquire一次 递归锁: RLock(),可以连续acquire多次,每acquire一次计数器+1,只有计数为0时,才能被抢到acquire # 死锁 f ...
- 图解Python 【第八篇】:网络编程-进程、线程和协程
本节内容一览图: 本章内容: 同步和异步 线程(线程锁.threading.Event.queue 队列.生产者消费者模型.自定义线程池) 进程(数据共享.进程池) 协程 一.同步和异步 你叫我去吃饭 ...
- GIL全局解释器锁-死锁与递归锁-信号量-event事件
一.全局解释器锁GIL: 官方的解释:掌握概念为主 """ In CPython, the global interpreter lock, or GIL, is a m ...
- 8.14 day32 TCP服务端并发 GIL解释器锁 python多线程是否有用 死锁与递归锁 信号量event事件线程q
TCP服务端支持并发 解决方式:开多线程 服务端 基础版 import socket """ 服务端 1.要有固定的IP和PORT 2.24小时不间断提供服务 3.能够支 ...
- 互斥锁 线程理论 GIL全局解释器锁 死锁现象 信号量 event事件 进程池与线程池 协程实现并发
目录 互斥锁 multiprocessing Lock类 锁的种类 线程理论 进程和线程对比 开线程的两种方式(类似进程) 方式1 使用Thread()创建线程对象 方式2 重写Thread类run方 ...
- Python 进程、线程、协程、锁机制,你知多少?
1.python的多线程到底有没有用? 2. 为什么在python里推荐使用多进程而不是多线程 3.进程.线程.协程.各种锁 4.Python多进程编程
- python 进程、线程与协程的区别
进程.线程与协程区别总结 - 1.进程是计算器最小资源分配单位 - 2.线程是CPU调度的最小单位 - 3.进程切换需要的资源很最大,效率很低 - 4.线程切换需要的资源一般,效率一般(当然了在不考虑 ...
- python基础之进程、线程、协程篇
一.多任务(多线程) 多线程特点:(1)线程的并发是利用cpu上下文的切换(是并发,不是并行)(2)多线程执行的顺序是无序的(3)多线程共享全局变量(4)线程是继承在进程里的,没有进程就没有线程(5) ...
- python并发编程之进程、线程、协程的调度原理(六)
进程.线程和协程的调度和运行原理总结. 系列文章 python并发编程之threading线程(一) python并发编程之multiprocessing进程(二) python并发编程之asynci ...
随机推荐
- 蓝鲸单机离线部署:app_mgr组件安装失败解决
之前在腾讯蓝鲸智云-单机离线部署测试中,遇到了几个安装问题,本文记录下3.2 app_mgr组件安装失败 的解决过程,因为这个问题卡了很久(可能也是因为笔者对python相关知识和蓝鲸产品不够熟悉), ...
- HBase-统计表总行数的三种方式
由于Hbase是列式数据库,没有提供类似SQL的数据查询语句,可以通过以下三种方式获取表的总行数. 1. 使用Hbase自带的Count命令 hbase提供了count命令可以在hbase交互界面使用 ...
- NC18985 数字权重
题目链接 题目 题目描述 小a有一个n位的数字,但是它忘了各个位上的数是什么,现在请你来确定各个位上的数字,满足以下条件: 设第i位的数为ai,其中a1为最高位,an为最低位,K为给定的数字 不含前导 ...
- NC20951 网络优化
题目链接 题目 题目描述 <梦三国2>是一款3D MOBA类网游.游戏继承<梦三国>的三国文化背景和基础玩法,并加入许多全新地图和全新竞技玩法.由于人气高,游戏在线人数与日俱增 ...
- CentOS8-pacemaker+corosync高可用部署
部署pacemaker yum install pacemaker pcs corosync fence-agents resource-agents 启动pcs服务 systemctl enable ...
- 用ELK分析每天4亿多条腾讯云MySQL审计日志(3)--下载日志
当初分析日志,麻烦的是腾讯云的SQL审计日志下载,有下列限制: 1,单次最多1000万条下载 2,单个实例最多生成5条日志文件,多的要先删除以前文件才能生成 腾讯云日志文件生成界面: 一 ...
- Java I/O 教程(七) DataOutputStream和DataInputStream
Java DataOutputStream Class Java DataOutputStream class 可以以机器无关方式往指定输出流写入Java原始数据类型,例如int, double, l ...
- SpringBoot中Redis的基础使用
基础使用 首先引入依赖 <!-- redis依赖--> <dependency> <groupId>org.springframework.boot</gro ...
- 多线程系列(七) -ThreadLocal 用法及内存泄露分析
一.简介 在 Java web 项目中,想必很多的同学对ThreadLocal这个类并不陌生,它最常用的应用场景就是用来做对象的跨层传递,避免多次传递,打破层次之间的约束. 比如下面这个HttpSer ...
- leetcode - 中序遍历
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 . 示例 1: 输入:root = [1,null,2,3] 输出:[1,3,2] 示例 2: 输入:root = [] 输出:[] 示例 ...