day36

死锁现象与递归锁

死锁现象

是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁

from threading import Thread
from threading import Lock
import time lock_A = Lock()
lock_B = Lock() class MyThread(Thread):
def run(self):
self.f1()
self.f2() def f1(self):
lock_A.acquire()
print(f"{self.name}拿到了A锁") lock_B.acquire()
print(f"{self.name}拿到了B锁") lock_B.release()
lock_A.release() def f2(self):
lock_B.acquire()
print(f"{self.name}拿到了B锁") time.sleep(0.1)
lock_A.acquire()
print(f"{self.name}拿到了A锁") lock_A.release()
lock_B.release() if __name__ == '__main__':
for i in range(3):
t = MyThread()
t.start()
结果:
Thread-1拿到了A锁
Thread-1拿到了B锁
Thread-1拿到了B锁
Thread-2拿到了A锁
未结束
递归锁

递归锁可以解决死锁现象,业务需要多个锁时,先要考虑递归锁

递归锁有一个计数的功能,原数字为0,上一次锁计数+1,释放一次锁计数-1

只要递归锁上面的数字不为零,其他线程就不能枪锁

总结定义:RLock,同一把锁,引用一次计数+1,释放一次计数-1,只要计数不为零,其他线程进程就抢不到,他能解决死锁问题

from threading import Thread
from threading import RLock
import time lock_B = lock_A = RLock() class MyThread(Thread):
def run(self):
self.f1()
self.f2() def f1(self):
lock_A.acquire()
print(f"{self.name}拿到了A锁") lock_B.acquire()
print(f"{self.name}拿到了B锁") lock_B.release()
lock_A.release() def f2(self):
lock_B.acquire()
print(f"{self.name}拿到了B锁") time.sleep(0.1)
lock_A.acquire()
print(f"{self.name}拿到了A锁") lock_A.release()
lock_B.release() if __name__ == '__main__':
for i in range(10):
t = MyThread()
t.start()

信号量

也是一种锁,控制并发数量

总结定义:同一时刻可以设置抢锁的线程或者进程数量

同进程的一样

Semaphore管理一个内置的计数器,

每当调用acquire()时内置计数器-1;

调用release() 时内置计数器+1;

计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()

实例:(同时只有5个线程可以获得semaphore,即可以限制最大连接数为5):

from threading import Thread, Semaphore, current_thread
import time
import random
sem = Semaphore(5) def task():
sem.acquire()
·
print(f"{current_thread().name} 厕所ing")
time.sleep(random.randint(1, 3)) sem.release() if __name__ == '__main__':
for i in range(20):
t = Thread(target=task)
t.start()

GIL全局解释器锁

总结定义:全局解释器锁,同一时刻只能一个线程进入解释器,Cpython解释器具有的。

好多自称大神的说,GIL锁是python的致命缺陷,python不能多核,并发不行等等。。。。

理论上来说:单个进程的多线程可以利用多核

但是开发Cpython解释器的程序员,给解释器加了锁

为什么加锁?

1、当时都是单核时代,而且cpu价格非常贵

2、如果不加全局解释器锁,开发Cpython解释器的程序员就会在源码内部各种主动加锁,解锁,非常麻烦,各种死锁现象等等,他为了省事就直接给解释器加了一个锁

  • 优点:保证了Cpython解释器的数据资源的安全
  • 缺点:单个进程的多线程不能利用多核

Jpython没有GIL锁

pypy也没有GIL锁

现在多核时代,我将Cpython的GIL去掉行不?

​ 因为Cpython解释器所有的业务逻辑都是围绕着单个线程实现的,去掉这个GIL锁,几乎不可能

单个进程的多线程可以并发,但是不能利用多核进行并行

多个进程可以并发,并行

IO密集型

计算密集型

GIL与lock锁的区别

相同点

都是同种锁,互斥锁

不同点
  • GIL锁是全局解释器锁,保护解释器内部的资源数据的安全
  • GIL锁,上锁,释放无需手动操作
  • 自己代码中定义的互斥锁保护进程中的资源数据的安全
  • 自己定义的互斥锁必须自己手动上锁,释放锁

验证计算密集型IO密集型的效率

计算密集型

单个进程的多线程并发 vs 多个进程的并发并行

总结:计算密集型:多进程的并发并行效率高

from threading import Thread
from multiprocessing import Process
import time
import random def task():
count = 0
for i in range(10000000):
count += 1 if __name__ == '__main__':
# 多进程的并发,并行
start_time = time.time()
l1 = []
for i in range(4):
p = Process(target=task)
l1.append(p)
p.start()
for j in l1:
j.join() print(f"执行效率:{time.time() - start_time}") # 1.5881953239440918 # 多线程的并发
start_time = time.time()
l1 = []
for i in range(4):
p = Thread(target=task)
l1.append(p)
p.start()
for j in l1:
j.join() print(f"执行效率:{time.time() - start_time}") # 5.415819883346558
IO密集型

IO密集型:单个进程的多线程并发 vs 多个进程的并发进行

对于IO密集型:单个进程的多线程的并发效率高

from threading import Thread
from multiprocessing import Process
import time
import random def task():
count = 0
time.sleep(random.randint(1, 3))
count += 1 if __name__ == '__main__':
# 多进程的并发,并行
start_time = time.time()
l1 = []
for i in range(50):
p = Process(target=task)
l1.append(p)
p.start()
for j in l1:
j.join() print(f"执行效率:{time.time() - start_time}") # 4.230581283569336 # 多线程的并发
start_time = time.time()
l1 = []
for i in range(50):
p = Thread(target=task)
l1.append(p)
p.start()
for j in l1:
j.join() print(f"执行效率:{time.time() - start_time}") # 3.011176347732544

多线程实现socket通信

server
import socket
from threading import Thread def _accept():
server = socket.socket()
server.bind(("127.0.0.1", 8848))
server.listen(5) while 1:
conn, addr = server.accept()
t = Thread(target=communicate, args=(conn, addr))
t.start() def communicate(conn, addr):
while 1:
try: from_client_data = conn.recv(1024)
print(f"来自客户端{addr[1]}的消息:{from_client_data.decode('utf-8')}")
to_client_data = input(">>>").strip()
conn.send(to_client_data.encode("utf-8"))
except Exception:
break
conn.close() if __name__ == '__main__':
_accept()
client
import socket
client = socket.socket()
client.connect(("127.0.0.1", 8848)) while 1:
try:
to_server_data = input(">>>").strip()
client.send(to_server_data.encode("utf-8")) from_server_data = client.recv(1024)
print(f"来自服务端的消息:{from_server_data.decode('utf-8')}")
except Exception:
break
client.close()

进程池、线程池

无论是多线程还是多进程,如果按照上面的写法,来一个客户端请求,我就开一个线程,来一个请求开一个线程

应该是这样:你的计算机允许范围内,开启的线程进程数量越多越好

线程池:一个容器,这个容器限制住你开启线程的数量,比如4个,第一次肯定只能并发的处理4个任务,只要有任务完成,线程马上就会接着执行下一个任务

进程池:一个容器,这个容器限制住你开启进程的数量,比如4个,第一次并行的处理4个任务,只要有任务完成,进程马上就会接着执行下一个任务

from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import os
import time
import random # print(os.cpu_count())
def task(n):
print(f"{os.getpid()}接客")
time.sleep(random.randint(1, 3)) if __name__ == '__main__':
# 开启进程池(并行+并发)
p = ProcessPoolExecutor(4) # 默认不写,进程池里面的进程数与cpu里面的内核个数相等
#
# # p.submit(task,1)
# # p.submit(task,1)
# # p.submit(task,1)
# # p.submit(task,1)
# # p.submit(task,1)
# # p.submit(task,1)
for i in range(22):
p.submit(task, 1) # 开启线程池 (并发)
# t = ThreadPoolExecutor() # 默认不写,cpu内核个数*5=线程数
t = ThreadPoolExecutor(8) # 100个线程
for i in range(50):
t.submit(task, i)

day36——死锁、递归锁、信号量、GIL、多线程实现socket通信、线程池和进程池的更多相关文章

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

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

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

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

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

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

  4. 同步锁 死锁与递归锁 信号量 线程queue event事件

    二个需要注意的点: 1 线程抢的是GIL锁,GIL锁相当于执行权限,拿到执行权限后才能拿到互斥锁Lock,其他线程也可以抢到GIL,但如果发现Lock任然没有被释放则阻塞,即便是拿到执行权限GIL也要 ...

  5. 线程锁&信号量&gil

    线程锁 线程锁的主要目的是防止多个线程之间出现同时抢同一个数据,这会造成数据的流失.线程锁的作用类似于进程锁,都是为了数据的安全性 下面,我将用代码来体现进程锁的作用: from threading ...

  6. Python进阶----线程基础,开启线程的方式(类和函数),线程VS进程,线程的方法,守护线程,详解互斥锁,递归锁,信号量

    Python进阶----线程基础,开启线程的方式(类和函数),线程VS进程,线程的方法,守护线程,详解互斥锁,递归锁,信号量 一丶线程的理论知识 什么是线程:    1.线程是一堆指令,是操作系统调度 ...

  7. GIL全局解释器锁,线程池与进程池 同步异步,阻塞与非阻塞,异步回调

    GIL全局解释器锁 1.什么是GIL 官方解释:'''In CPython, the global interpreter lock, or GIL, is a mutex that prevents ...

  8. 多进程 multiprocessing 多线程Threading 线程池和进程池concurrent.futures

    multiprocessing.procsess 定义一个函数 def func():pass 在if __name__=="__main__":中实例化 p = process( ...

  9. 并发编程:GIL,线程池,进程池,阻塞,非阻塞,同步,异步

    一  GIL(global interpreter lock) GIL中文叫全局解释器锁,我们执行一个文件会产生一个进程,那么我们知道进程不是真正的执行单位,而是资源单位,所以进程中放有解释器(cpy ...

随机推荐

  1. python中序列的操作

    Python中的序列操作 可变对象:列表.字典.集合 不可变对象:数值.字符串.元组.forzenset 1.序列的通用操作 (1)测试元素是否存在 x in S和x not in S,返回True或 ...

  2. edgedb-js 来自官方的js 驱动

    目前对于edgedb 主要还是来自官方的python驱动,目前js 版本的已经快发布了,代码在github 可以看到了 同时官方文档也提供了一个关于edgedb 内部的协议说明,结合js 驱动以及文档 ...

  3. vscode vue文件格式化没效果

    在vscode 中   格式化vue文件没效果 解决办法: 点击头部文件 >首选项>设置 在右侧加入这两句 "vetur.format.defaultFormatter.js&q ...

  4. (浙江金华)Day 1 组合数计数

    目录 Day 1 组合计数 1.组合数 (1).C(n,m) 读作n选m,二项式系数 : (2).n个东西里选m个的方案数 不关心选的顺序: (3).二项式系数--->多项式系数: 2.组合数计 ...

  5. Codevs 2009 大dota英雄 2013年省队选拔赛辽宁(状压DP)

    2009 大dota英雄 2013年省队选拔赛辽宁 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 话说退役后的生活好无聊啊,以 ...

  6. 如何防范web前端安全攻击

    一.对于XSS防御: 1.不要信任任何外部传入的数据,针对用户输入作相关的格式检查.过滤等操作,以及转义字符处理.最普遍的做法就是转义输入输出的内容,对于括号,尖括号,斜杠进行转义 function ...

  7. vue+Element 后台管理骨架

    1.使用的是vue-cli 3.0起的 2.文件目录结构 3.整体的骨架是根据element 文档里头的Container容器布局来的(复制粘贴,喜欢什么色儿就改) aside这个 部分需要注意的是这 ...

  8. 解决PHP7无法监听9000端口问题/502错误解决办法

    问题背景 昨晚帮配置nginx+php服务的时候,发生了一个奇怪的事情netstat -anp|grep 9000查看9000端口,一直没有监听,于是nginx无法通过fastcgi来代理php请求. ...

  9. Centos 安装 zookeeper

    下载 下载地址:http://archive.apache.org/dist/zookeeper/ [root@localhost bin]# wget http://archive.apache.o ...

  10. Java基础 try...catch(多个异常) 多个异常采取同样的解决措施

        JDK :OpenJDK-11      OS :CentOS 7.6.1810      IDE :Eclipse 2019‑03 typesetting :Markdown   code ...