python 36 进程池、线程池
1. 死锁与递归锁
死锁:两个或者两个以上的进程或者线程在执行过程中,因争夺资源而造成的一种等待现象,称为死锁现象。
递归锁可以解决死锁现象。
递归锁有一个计数的功能,原数字为0,锁一次计数+1,释放一次,计数-1;只要数字不为0,其他线程就不能枪锁。
from threading import RLock
from threading import Thread
import time
lock_A = lock_B = 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(3):
t = MyThread()
t.start()
2. 信号量Semaphor
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
能够控制同一时刻的线程数。
from threading import Thread, Semaphore, current_thread
import time
import random
sem = Semaphore(5) # 设置同一时刻只能有5个线程并发执行
def task():
sem.acquire()
print(f'{current_thread().name}进程在运行')
time.sleep(random.random())
sem.release()
if __name__ == '__main__':
for i in range(30):
t = Thread(target=task)
t.start()
3. GIL全局解释器锁:(Cpython)
Cpython规定,同一时刻只允许一个线程进入解释器,因为加了GIL。
如果不加全局解释器锁,会出现死锁现象,开发此的程序员为了方便,在进入解释器时给线程加了一个全局解释器锁,这样保证了解释器内的数据安全。
优点: 保证了Cpython解释器的数据资源的安全;
缺点: 单个进程的多线程不能利用多核cpu(缺陷)。
Jython、pypy解释器没有GIL全局解释器锁。
流程:当线程遇到IO阻塞,cpu就会无情的被操作系统切走,GIL全局解释器锁被释放,线程挂起,另一个线程进入,这样可以实现并发。

总结:
单个进程的多线程可以并发执行,但是不能利用cpu多核并行执行;
多个进程可以并发、并行执行。
4. IO、计算密集型对比
4.1 计算密集型:
单个进程的多线程 VS 多个进程的并发、并行
from threading import Thread
from multiprocessing import Process
import time
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 = Thread(target=task,)
l1.append(p)
p.start()
for p in l1:
p.join()
print(f'执行效率:{time.time()- start_time}') # 1.9125
# 多进程并发、并行
if __name__ == '__main__':
start_time = time.time()
l1 = []
for i in range(4):
p = Process(target=task,)
l1.append(p)
p.start()
for p in l1:
p.join()
print(f'执行效率:{time.time()- start_time}') # 0.86031
# 总结:
计算密集型:多进程并发、并行效率高。
4.2 IO密集型
单个进程的多线程 VS 多个进程的并发并行
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 p in l1:
p.join()
print(f'执行效率:{time.time()- start_time}') # 4.52878
# 多线程并发
start_time = time.time()
l1 = []
for i in range(50):
p = Thread(target=task, )
l1.append(p)
p.start()
for p in l1:
p.join()
print(f'执行效率:{time.time() - start_time}') # 3.00845
# 总结:
对于IO密集型:单个进程的多线程的并发效率高。
5. GIL与Lock锁的区别
相同点:都是互斥锁;
GIL: 保护解释器内部的资源数据的安全,释放无需手动操作;
Lock:自定义锁,保护进程中的数据资源安全。需要手动操作。
6. 多线程实现socket通信
服务端每连接到一个客户端时,都会开启一个线程进行通信。
# server端
from threading import Thread
import socket
def accept():
server = socket.socket()
server.bind(("127.0.0.1", 8888))
server.listen(5)
while 1:
conn, addr = server.accept()
# 连接一个开启一个线程
t = Thread(target=communication, args=(conn, addr))
t.start()
def communication(conn, addr):
while 1:
try:
from_client_data = conn.recv(1024)
print(f"来自客户端{addr}的消息:{from_client_data.decode('utf-8')}")
to_client_data = input(">>>").strip().encode("utf-8")
conn.send(to_client_data)
except Exception:
break
conn.close()
if __name__ == '__main__':
accept()
# client端
import socket
client = socket.socket()
client.connect(('127.0.0.1', 8888))
while 1:
try:
to_server_data = input(">>>").strip().encode("utf-8")
client.send(to_server_data)
from_server_data = client.recv(1024)
print(f"来自客户端的消息:{from_server_data.decode('utf-8')}")
except Exception:
break
client.close()
7. 进程池、线程池
以时间换取空间。
概念:定义一个池子,在里面放上固定数量的进程(线程),有需求来了,就拿一个池中的进程(线程)来处理任务,等到处理完毕,进程(线程)并不关闭,而是将进程(线程)再放回池中继续等待任务。如果有很多任务需要执行,池中的进程(线程)数量不够,任务就要等待之前的进程(线程)执行任务完毕归来,拿到空闲的进程(线程)才能继续执行。也就是说,池中进程(线程)的数量是固定的,那么同一时间最多有固定数量的进程(线程)在运行。这样不会增加操作系统的调度难度,还节省了开闭进程(线程)的时间,也一定程度上能够实现并发效果。
官网:https://docs.python.org/dev/library/concurrent.futures.html
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import os
import time
import random
def task(i):
print(f"第{i}:{os.getpid()}开始执行!")
time.sleep(random.random())
if __name__ == '__main__':
# 线程池
t = ThreadPoolExecutor() # 开启线程池 不写默认最大处理20个线程
for i in range(40):
t.submit(task, i+1) # 开启线程
# 进程池
p = ProcessPoolExecutor() # 开启进程池 不写默认处理cpu个数的进程
for i in range(20):
p.submit(task, i+1)
示例:使用线程池实现socket通信
# server端
from concurrent.futures import ThreadPoolExecutor
import socket
def accept():
server = socket.socket()
server.bind(("127.0.0.1", 8888))
server.listen(5)
t = ThreadPoolExecutor(2) # 设置最大2个线程的线程池
while 1:
conn, addr = server.accept()
t.submit(communication, conn, addr)
def communication(conn, addr):
while 1:
try:
from_client_data = conn.recv(1024)
print(f"来自客户端{addr}的消息:{from_client_data.decode('utf-8')}")
to_client_data = input(">>>").strip().encode("utf-8")
conn.send(to_client_data)
except Exception:
break
conn.close()
if __name__ == '__main__':
accept()
# client端
import socket
client = socket.socket()
client.connect(('127.0.0.1', 8888))
while 1:
try:
to_server_data = input(">>>").strip().encode("utf-8")
client.send(to_server_data)
from_server_data = client.recv(1024)
print(f"来自客户端的消息:{from_server_data.decode('utf-8')}")
except Exception:
break
client.close()
python 36 进程池、线程池的更多相关文章
- 《转载》Python并发编程之线程池/进程池--concurrent.futures模块
本文转载自Python并发编程之线程池/进程池--concurrent.futures模块 一.关于concurrent.futures模块 Python标准库为我们提供了threading和mult ...
- python自带的线程池和进程池
#python自带的线程池 from multiprocessing.pool import ThreadPool #注意ThreadPool不在threading模块下 from multiproc ...
- python并发编程-进程池线程池-协程-I/O模型-04
目录 进程池线程池的使用***** 进程池/线程池的创建和提交回调 验证复用池子里的线程或进程 异步回调机制 通过闭包给回调函数添加额外参数(扩展) 协程*** 概念回顾(协程这里再理一下) 如何实现 ...
- Python学习之GIL&进程池/线程池
8.6 GIL锁** Global interpreter Lock 全局解释器锁 实际就是一把解释器级的互斥锁 In CPython, the global interpreter lock, or ...
- Python并发编程05 /死锁现象、递归锁、信号量、GIL锁、计算密集型/IO密集型效率验证、进程池/线程池
Python并发编程05 /死锁现象.递归锁.信号量.GIL锁.计算密集型/IO密集型效率验证.进程池/线程池 目录 Python并发编程05 /死锁现象.递归锁.信号量.GIL锁.计算密集型/IO密 ...
- Python标准模块--concurrent.futures 进程池线程池终极用法
concurrent.futures 这个模块是异步调用的机制concurrent.futures 提交任务都是用submitfor + submit 多个任务的提交shutdown 是等效于Pool ...
- concurrent.futures模块(进程池/线程池)
需要注意一下不能无限的开进程,不能无限的开线程最常用的就是开进程池,开线程池.其中回调函数非常重要回调函数其实可以作为一种编程思想,谁好了谁就去掉 只要你用并发,就会有锁的问题,但是你不能一直去自己加 ...
- Python-GIL 进程池 线程池
5.GIL vs 互斥锁(*****) 1.什么是GIL(Global Interpreter Lock) GIL是全局解释器锁,是加到解释器身上的,保护的就是解释器级别的数据 (比如垃圾回收的数据) ...
- 13 并发编程-(线程)-异步调用与回调机制&进程池线程池小练习
#提交任务的两种方式 #1.同步调用:提交完任务后,就在原地等待任务执行完毕,拿到结果,再执行下一行代码,导致程序是串行执行 一.提交任务的两种方式 1.同步调用:提交任务后,就在原地等待任务完毕,拿 ...
- 并发编程---线程queue---进程池线程池---异部调用(回调机制)
线程 队列:先进先出 堆栈:后进先出 优先级:数字越小优先级越大,越先输出 import queue q = queue.Queue(3) # 先进先出-->队列 q.put('first') ...
随机推荐
- [HDOJ] 2026.Max Sum
2026.Max Sum (c++) Problem Description Consider the aggregate An= { 1, 2, -, n }. For example, A1={1 ...
- 2019杭电多校第一场hdu6581 Vacation
Vacation 题目传送门 update(O(n)) 看了那个O(n)的方法,感觉自己想的那个O(nlogn)的好傻,awsl. 0车最终通过停车线的时候,状态一定是某个车堵住后面的所有车(这个车也 ...
- 数据库---T-SQL语句(一)
一.T-SQL语句 1.创建表:create table Name(Code varchar(50),) 主键:primary key 自增长:auto_increment 外键关系:referenc ...
- java - tcp如何保证传输的可靠性和有序性
TCP提供的是一种面向连接的,可靠性的字节流服务. 可靠性: 1.应用数据被TCP划分为最适合发送的数据包: 2.在TCP发送一个数据块后,将启动一个定时器,用以接收目的端的确认信息,若不能及时得到确 ...
- Python3发送邮件功能
Python3实现邮件发送功能 import smtplib from email.mime.text import MIMEText # 导入模块 class SendEmail: def send ...
- 3、大型项目的接口自动化实践记录----开放API练习
开始做实际项目前,先拿个网上的简单API练下手 一.API说明: 接口信息 接口名:京东获取单个商品价格 地址:http://p.3.cn/prices/mgets 入参:skuids=J_商品ID& ...
- Eclipse中代码自动添加注释及代码注释模板
介绍 为了提高代码的可读性以及为了有些代码有洁癖的人的需求,我们要从学生到职业进行迈进的过程中,必须把以前的那种代码可读性不高的习惯改掉,因为我们必须要与企业接轨.. 好了,废话不多说,反正就是提升自 ...
- java多线程基础(二)--sleep(),wait,()yield()和join()方法
1.sleep()方法 在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”.不推荐使用. sleep()使当前线程进入阻塞状态,在指定时间内不会执行. 2.wait()方法 在其他线程调用 ...
- 算法与数据结构基础 - 二叉树(Binary Tree)
二叉树基础 满足这样性质的树称为二叉树:空树或节点最多有两个子树,称为左子树.右子树, 左右子树节点同样最多有两个子树. 二叉树是递归定义的,因而常用递归/DFS的思想处理二叉树相关问题,例如Leet ...
- [Spring cloud 一步步实现广告系统] 17. 根据流量类型查询广告
广告检索服务 功能介绍 媒体方(手机APP打开的展示广告,走在路上看到的大屏幕广告等等) 请求数据对象实现 从上图我们可以看出,在媒体方向我们的广告检索系统发起请求的时候,请求中会有很多的请求参数信息 ...