一 死锁和递归锁(了解)

进程也有死锁与递归锁,使用方法类似

所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

当你知道锁的使用抢锁必须要释放锁,其实你在操作锁的时候也极其容易产生死锁现象
(整个程序卡死 阻塞) # 递归锁
递归锁的特点
可以被连续的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
从而提升程序的运行效率

总结协程特点:

  1. 必须在只有一个单线程里实现并发
  2. 修改共享数据不需加锁
  3. 用户程序里自己保存多个控制流的上下文栈
  4. 附加:一个协程遇到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事件、进程池、线程池、协程)的更多相关文章

  1. python并发编程-多线程实现服务端并发-GIL全局解释器锁-验证python多线程是否有用-死锁-递归锁-信号量-Event事件-线程结合队列-03

    目录 结合多线程实现服务端并发(不用socketserver模块) 服务端代码 客户端代码 CIL全局解释器锁****** 可能被问到的两个判断 与普通互斥锁的区别 验证python的多线程是否有用需 ...

  2. 并发编程---死锁||递归锁---信号量---Event事件---定时器

    死锁 互斥锁:Lock(),互斥锁只能acquire一次 递归锁:  RLock(),可以连续acquire多次,每acquire一次计数器+1,只有计数为0时,才能被抢到acquire # 死锁 f ...

  3. 图解Python 【第八篇】:网络编程-进程、线程和协程

    本节内容一览图: 本章内容: 同步和异步 线程(线程锁.threading.Event.queue 队列.生产者消费者模型.自定义线程池) 进程(数据共享.进程池) 协程 一.同步和异步 你叫我去吃饭 ...

  4. GIL全局解释器锁-死锁与递归锁-信号量-event事件

    一.全局解释器锁GIL: 官方的解释:掌握概念为主 """ In CPython, the global interpreter lock, or GIL, is a m ...

  5. 8.14 day32 TCP服务端并发 GIL解释器锁 python多线程是否有用 死锁与递归锁 信号量event事件线程q

    TCP服务端支持并发 解决方式:开多线程 服务端 基础版 import socket """ 服务端 1.要有固定的IP和PORT 2.24小时不间断提供服务 3.能够支 ...

  6. 互斥锁 线程理论 GIL全局解释器锁 死锁现象 信号量 event事件 进程池与线程池 协程实现并发

    目录 互斥锁 multiprocessing Lock类 锁的种类 线程理论 进程和线程对比 开线程的两种方式(类似进程) 方式1 使用Thread()创建线程对象 方式2 重写Thread类run方 ...

  7. Python 进程、线程、协程、锁机制,你知多少?

    1.python的多线程到底有没有用? 2. 为什么在python里推荐使用多进程而不是多线程 3.进程.线程.协程.各种锁 4.Python多进程编程

  8. python 进程、线程与协程的区别

    进程.线程与协程区别总结 - 1.进程是计算器最小资源分配单位 - 2.线程是CPU调度的最小单位 - 3.进程切换需要的资源很最大,效率很低 - 4.线程切换需要的资源一般,效率一般(当然了在不考虑 ...

  9. python基础之进程、线程、协程篇

    一.多任务(多线程) 多线程特点:(1)线程的并发是利用cpu上下文的切换(是并发,不是并行)(2)多线程执行的顺序是无序的(3)多线程共享全局变量(4)线程是继承在进程里的,没有进程就没有线程(5) ...

  10. python并发编程之进程、线程、协程的调度原理(六)

    进程.线程和协程的调度和运行原理总结. 系列文章 python并发编程之threading线程(一) python并发编程之multiprocessing进程(二) python并发编程之asynci ...

随机推荐

  1. IIS的详细配置

    一:配置默认文档 输入ip打开哪个页面是由默认文档设定的 1.打开IIS配置页面,点击网站.我们的默认站点已经启动,可以看到绑定的ip和网页的路径 2.选中Default Web Site,可以看到有 ...

  2. CF1849

    传送门 A 氵 B 在吃了五次罚时后,我终于放弃了卡常优先队列,并发现:把余 \(0\) 看作余 \(k\),答案就是余数从大到小排列的,每种余数内部又按照下标排序. C 我为什么没想到哈希?自我检讨 ...

  3. 写好C#代码的技巧

    写好C#代码的技巧 编者导语 本文来自https://www.pluralsight.com,作者Afzaal Ahmad Zeeshan. 原文包含以下三篇文章: <编写更好的C#代码简介&g ...

  4. react 高效高质量搭建后台系统 系列

    react 高效高质量搭建后台系统 前言 目标:用 react 高效高质量搭建后台系统 如何实现:搞定一个优秀的.通用的.有一定复杂度的react的后台系统.类似项目就可以依葫芦画瓢快速展开. spu ...

  5. MySQL Unknown error 1267

    1.问题说明 最近在mysql中运行一段SQL直接报错: 有一点要说一下,这个navicat给出的报错太简短只有错误码,还得自己去查有点垃圾,不知道新版如何? 2.问题原因 这里可以看到问题出在t2. ...

  6. 利用LiveReload插件实现vscode和谷歌浏览器实时刷新

    说明 最近在研究CSS希望可以提升一个层次.在写DEMO练习的时候老是需要去谷歌浏览器手动刷新页面才能看到更改后的效果次数多了 我也受不了,这不我又请来了个帮手: LiveReload,名如其人,这家 ...

  7. 《深入理解Java虚拟机》(六) 调优策略 -- 笔记

    目录 一.操作文档类功能,大量大对象直接进入老年代 问题现象 解决方法 通过单一Java虚拟机管理大量的内存 同一台服务器上部署若干Java虚拟机 二.异步请求大量累积 三.排查问题 排查问题: 可能 ...

  8. docker启动nginx https自签名证书配置

    启动测试应用时, 有时需要自己配置证书签名: 1 使用系统自带openssl openssl req \ > -x509 \ > -nodes \ > -days 365 \ > ...

  9. win32 - 在进程之间获取事件通知(CreateEvent)

    只需要记住使用OpenEvent来同步Event对象. Project A: #define _CRT_SECURE_NO_WARNINGS #include <Windows.h> #i ...

  10. 【Android 逆向】【攻防世界】人民的名义-抓捕赵德汉1-200

    1. 这一题下载下来是个jar文件,感觉很android关系不大,但还是放在了mobile这个分类下了 2. 直接java jar运行,提示需要输入密码 # java -jar 169e139f152 ...