python day 20: 线程池与协程,多进程TCP服务器
python day 20: 线程池与协程
2019/11/1
资料来自老男孩教育
2. 线程
线程适用于IO密集流操作,线程是最小的执行单元
线程之间数据是共享的,共用一块内存
import threading :导入线程模块
t = threading.Thread(target=func,args=())
t.start()
t.join([timeout]),此处join是wait的意思,即主线程等待子线程结束,最多等2秒
主进程是否等待子线程
t.setDaemon(True/False)
线程锁:使用RLOCK
mutex = threading.RLock()创建锁
mutex.acquire()获得锁
mutex.release()释放锁
Event事件
event_obj = threading.Event()创建事件对象
event_obj.wait()等待状态,flag为False则阻塞所有的线程
event_obj.clear()将flag设置为False
event_obj.set()将flal设置为True
3. 进程
创建进程
import multiprocessing
if name=="main":
p = multiprocessing.Process(target=func,args=(),)
p.deamon = True/False(主进程是否等待子进程结束)
p.start()
p.join([timeout])主进程等待子进程结束,最多等待多少秒
进程之间数据不共享,每个进程使用独立内存
进程间数据共享的实现方式Manage或Array
Array
数组必须一开始就定义好数组的长度
数组的元素必须是统一的数据类型
Manage()
manage = Manage()
manage.dict()无长度限制,比Array方便,进程间数据通信使用manage.dict是最优选择。
进程池Pool
pool = multiprocessing.Pool(5),创建一个最多支持5个进程的进程池。
pool.apply(func,(1,))申请一个进程执行某个函数。每个任务是排队执行。每个进程有join执行。
pool.apply_async(func=Foo,args=(1,),callback=Bar),申请一个进程去执行Foo方法,将Foo方法的返回值作为实参赋值给Bar函数。每个任务是并发执行。更多是使用apply_async。每个进程没有join执行. 进程的deamon=True。
pool.close()关闭进程池,不再接收新请求。或pool.terminate()
pool.join(),进程池中进程执行完毕后主进程再关闭。
4. 协程:gevent模块,又叫微线程
必须安装greenlet模块与gevent模块
gevent代表高性能,当发IO请求时,尤其是网络IO请求时,使用协程
gevent.sleep(0)
gevent.joinall()
from gevent import monkey; monkey.patch_all()
import requests
import gevent
def f(url):
print("url: %s"%url)
ret = requests.get(url)
data = ret.text
print(url,len(data))
gevent.joinall([
gevent.spawn(f,"https://juejin.im/post/5aa7314e6fb9a028d936d2a4"),
gevent.spawn(f,"https://www.cnblogs.com/lanxing0422/p/pythonday19.html"),
gevent.spawn(f,"http://www.baidu.com"),
])
# # 用于IO密集流
# def foo():
# print("foo")
# # 执行gevent的sleep方法
# gevent.sleep(0)
# print("foo again")
#
# def bar():
# print("bar")
# gevent.sleep(0)
# print("bar again")
#
# gevent.joinall([
# gevent.spawn(foo),
# gevent.spawn(bar),
# ])
5. 扩展
生产者消费者模型
队列:Queue
队列的特性:先进先出
q = queue.Queue(max)
q.put()
q.get()
q.get_nowait
q.put_nowait()
rabbit_m_queue开源的,特别牛逼的
6. 自定义线程池
python内部没有提供,需要自定义。
实际使用中,更多是基于线程池来使用,而不是创建一个单独的线程。
非常重要,以后会经常使用。
import threading
import queue
import time
import contextlib
Stopevent = object()
class ThreadPool(object):
'''
进程池
'''
def __init__(self, max_num):
self.max_num = max_num
self.q = queue.Queue()
self.terminate_flag = False
# 真实创建的线程列表
self.generate_list = []
# 空闲中的线程列表
self.free_list = []
def generate_thread(self):
'''
创建一个线程执行任务
:return:
'''
t = threading.Thread(target=self.call)
t.start()
def run(self, func, args, callback=None):
'''
线程池执行一个任务
:param func: 任务函数名
:param args: 任务函数所需参数元组
:param callback: 任务执行失败或成功后执行的回调函数
:return: 如果线程池已经终止,则返回True否则None
'''
if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:
self.generate_thread()
tuple_obj = (func, args, callback)
self.q.put(tuple_obj)
def call(self):
'''
循环获取任务并执行
:return:
'''
# 获取当前线程并添加到列表中
current_thread = threading.currentThread()
self.generate_list.append(current_thread)
# 从队列中取任务
event = self.q.get()
# 当该任务的类型不是Stopevent时,死循环
while event != Stopevent:
func, args, callback = event
status = True
try:
ret = func(*args)
except Exception as e:
status = False
ret = e
# 当回调函数不为空时,就执行回调函数
if callback:
try:
callback(status, ret)
except Exception as e:
pass
# self.free_list.append(current_thread)
# event = self.q.get()
# self.free_list.remove(current_thread)
with self.work_state(self.free_list,current_thread):
if not self.terminate_flag:
event = self.q.get()
else:
event = Stopevent
else:
self.generate_list.remove(current_thread)
def close(self):
num = len(self.generate_list)
while num:
self.q.put(Stopevent)
num -= 1
def terminate(self):
self.terminate_flag = True
# 终止线程,不清空队列
# max = len(self.generate_list)
# while max:
# self.q.put(Stopevent)
# max -= 1
# self.q.empty()
# 终止线程且清空队列
while self.generate_list:
self.q.put(Stopevent)
self.q.empty()
@contextlib.contextmanager
def work_state(self,state_list,work_thread):
'''用于记录线程池中正在等待的线程数'''
state_list.append(work_thread)
try:
yield
finally:
state_list.remove(work_thread)
def foo(i):
time.sleep(0.1)
print("当前i是>>>",i)
return i + 100
def call_back(status, ret):
print("ret:>>>", ret)
pool = ThreadPool(10)
for i in range(50):
'''
将任务放进队列中:
创建线程:
只有空闲线程列表为空且创建的线程数量小于最大线程数时才会创建线程。
从队列中取到任务,线程执行任务
'''
pool.run(func=foo, args=(i,), callback=call_back)
pool.close()
print("gene_list",len(pool.generate_list))
7. 实现多进程TCP服务器
serSocket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
重新设置套接字选项,重复使用绑定的信息
当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到SO_REUSEADDR选项。
from socket import *
from multiprocessing import *
# 处理客户端的请求并为其服务
def dealwithClient(newSocket):
while True:
recvData = newSocket.recv(1024)
if len(recvData)==0:
break
else:
print(recvData)
newSocket.close()
def main():
serSock = socket(AF_INET,SOCK_STREAM)
# 设置套接字选项,使其可以重复使用绑定的信息
serSock.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
bindAddr= ('',9091)
serSock.bind(bindAddr)
serSock.listen(5)
try:
while True:
newSocket,clientAddr = serSock.accept()
p1 = Process(target= dealwithClient,args=(newSocket,))
p1.start()
# 因为已经向⼦进程中copy了⼀份(引⽤) ,
# 并且⽗进程中这个套接字也没有用处了
# 所以关闭
newSocket.close()
finally:
serSock.close()
if __name__=='__main__':
main()
8. 实现多线程TCP服务器
from socket import *
from threading import *
# 处理客户端的请求并为其服务
def dealwithClient(newSocket):
while True:
recvData = newSocket.recv(1024)
if len(recvData)==0:
break
else:
print(recvData)
newSocket.close()
def main():
serSock = socket(AF_INET,SOCK_STREAM)
# 设置套接字选项,使其可以重复使用绑定的信息
serSock.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
bindAddr= ('',9091)
serSock.bind(bindAddr)
serSock.listen(5)
try:
while True:
newSocket,clientAddr = serSock.accept()
p1 = Thread(target= dealwithClient,args=(newSocket,))
p1.start()
# 因为线程中共享这个套接字, 如果关闭了会导致这个套接字不可⽤,
# 但是此时在线程中这个套接字可能还在收数据, 因此不能关闭
# newSocket.close()
finally:
serSock.close()
if __name__=='__main__':
main()
9. 协程greenlet和gevent
⽐线程更⼩的执⾏单元(微线程)
⼀个线程作为⼀个容器⾥⾯可以放置多个协程
只切换函数调用即可实现多任务,可以减少CPU的切换
协程⾃⼰主动让出CPU
使用生成器,只切换函数调用即可完成多任务切换
import time
def A():
while True:
print(“----A---”)
yield
time.sleep(0.5)
def B(c):
while True:
print(“----B---”)
c.next()
time.sleep(0.5)
if __name__==‘__main__’:
a = A() # 如果一个函数中有yield,返回值就是一个生成器
B(a)
# python中的greenlet模块对协程进行了封装(底层相当于yield)
# 安装模块: pip3 install greenlet
from greenlet import greenlet
import time
def t1():
while True:
print("........A........")
gr2.switch()
time.sleep(1)
def t2():
while True:
print("........b........")
gr1.switch()#调到上次执行的地方继续执行
time.sleep(1)
gr1 = greenlet(t1)#创建一个greenlet对象
gr2 = greenlet(t2)
gr1.switch()#此时会执行1函数
python还有⼀个⽐greenlet更强⼤的并且能够⾃动切换任务的模块 gevent.
原理是当⼀个greenlet遇到IO(指的是input output 输⼊输出)操作时, ⽐如访问⽹络, 就⾃动切换到其他的greenlet, 等到IO操作完成, 再在适当的时候切换回来继续执⾏.
io密集型和cpu密集型:一些进程绝大多数时间在计算上,称为计算密集型(CPU密集型),此时用多进程.
有一些进程则在input 和output上花费了大多时间,称为I/O密集型。比如搜索引擎大多时间是在等待(耗时操作),相应这种就属于I/O密集型。此时用多线程.
import gevent
def A():
while True:
print(".........A.........")
gevent.sleep(1)#用来模拟一个耗时操作
#gevent中:当一个协程遇到耗时操作会自动交出控制权给其他协程
def B():
while True:
print(".........B.........")
gevent.sleep(1)#每当遇到耗时操作,会自用转到其他协程
g1 = gevent.spawn(A) # 创建一个gevent对象(创建了一个协程),此时就已经开始执行A
g2 = gevent.spawn(B)
g1.join() #等待协程执行结束
g2.join() #会等待协程运行结束后再退出
python day 20: 线程池与协程,多进程TCP服务器的更多相关文章
- 进程池和线程池、协程、TCP单线程实现并发
一.进程池和线程池 当被操作对象数目不大时,我们可以手动创建几个进程和线程,十几个几十个还好,但是如果有上百个上千个.手动操作麻烦而且电脑硬件跟不上,可以会崩溃,此时进程池.线程池的功效就能发挥了.我 ...
- 并发编程(六)——进程/线程池、协程、gevent第三方库
进程/线程池.协程.gevent第三方库 一.进程/线程池 1.进程池 (1)什么是进程池 如果需要创建的子进程数量不大,可以直接利用multiprocess中的Process来创建.但是当需要创建上 ...
- python3下multiprocessing、threading和gevent性能对比----暨进程池、线程池和协程池性能对比
python3下multiprocessing.threading和gevent性能对比----暨进程池.线程池和协程池性能对比 标签: python3 / 线程池 / multiprocessi ...
- 并发编程 --进、线程池、协程、IO模型
内容目录: 1.socket服务端实现并发 2.进程池,线程池 3.协程 4.IO模型 1.socket服务端实现并发 # 客户端: import socket client = socket.soc ...
- 进程池与线程池、协程、协程实现TCP服务端并发、IO模型
进程池与线程池.协程.协程实现TCP服务端并发.IO模型 一.进程池与线程池 1.线程池 ''' 开进程开线程都需要消耗资源,只不过两者比较的情况下线程消耗的资源比较少 在计算机能够承受范围内最大限度 ...
- Python 37 进程池与线程池 、 协程
一:进程池与线程池 提交任务的两种方式: 1.同步调用:提交完一个任务之后,就在原地等待,等任务完完整整地运行完毕拿到结果后,再执行下一行代码,会导致任务是串行执行 2.异步调用:提交完一个任务之后, ...
- 并发编程(六)--进程/线程池、协程、gevent第三方库
一.进程/线程池 1.进程池 (1)什么是进程池 如果需要创建的子进程数量不大,可以直接利用multiprocess中的Process来创建.但是当需要创建上百个或上千个,手动创建就较为繁琐,这时就可 ...
- concurrent.futures进线程池和协程
concurrent.futures 异步执行进程线程池的模块,一个抽象类,定义submit,map,shutdown方法 from concurrent.futures import Process ...
- 并发编程中死锁、递归锁、进程/线程池、协程TCP服务器并发等知识点
1.死锁 定义; 类似两个人分别被囚禁在两间房子里,A手上拿着的是B囚禁房间的钥匙,而B拿着A的钥匙,两个人都没法出去,没法给对方开锁,进而造成死锁现象.具体例子代码如下: # -*-coding:u ...
随机推荐
- SEAndroid
SEAndroid安全机制所要保护的对象是系统中的资源,这些资源分布在各个子系统中,例如我们经常接触的文件就是分布文件子系统中的. 实际上,系统中需要保护的资源非常多,除了前面说的文件之外,还有进程. ...
- SpringBoot(十六):SpringBoot2.1.1集成fastjson,并使用fastjson替代默认的MappingJackson
springboot2.1.1默认采用的json converter是MappingJackson,通过调试springboot项目中代码可以确定这点.在springboot项目中定义WebMvcCo ...
- 【Laravel基础】laravel基础之相关概念,自定义服务提供者:Contracts, ServiceContainer, ServiceProvider, Facades关系
Contracts, ServiceContainer, ServiceProvider, Facades关系 概念 Contracts 合同,契约,也就是接口,定义一些规则,每个实现此接口的都要实现 ...
- Hash算法及java HashMap底层实现原理理解(含jdk 1.7以及jdk 1.8)
现在很多公司面试都喜欢问java的HashMap原理,特在此整理相关原理及实现,主要还是因为很多开发集合框架都不甚理解,更不要说各种其他数据结构了,所以造成面子造飞机,进去拧螺丝. 1.哈希表结构的优 ...
- Go1.13 标准库的 http 包爆出重大 bug,你的项目中招了吗? 原创: 王亚楼 Go语言中文网 今天
Go1.13 标准库的 http 包爆出重大 bug,你的项目中招了吗? 原创: 王亚楼 Go语言中文网 今天
- Android ANR log trace日志文件分析
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qq_25804863/article/ ...
- jeecg数据库切换至mysql8.0方式
1.修改pom.xml mysql版本 <mysql.version>8.0.11</mysql.version> 2.修改dbconfig.properties文件 hi ...
- yum提示problem making ssl connection的解决办法
yum缓存提示problem making ssl connection的解决办法 缺少ssl证书认证本地获取的问题导致,解决办法如下: 执行命令:yum install -y ca-certific ...
- Android之WebRTC介绍(二)
WebRTC提供了点对点之间的通信,但并不意味着WebRTC不需要服务器.暂且不说基于服务器的一些扩展业务,WebRTC至少有两件事必须要用到服务器: 1. 浏览器之间交换建立通信的元数据(信令)必须 ...
- qt linux 打包
本文在银河麒麟上成功运行,程序类型:Qt控制台,使用到的Qt外库:mysql数据库 1.环境一共有两台,1是编译机[装有Qt.数据库],2是运行机[纯净机] 2.在编译机上安装Qt.mysql,我这里 ...