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服务器的更多相关文章

  1. 进程池和线程池、协程、TCP单线程实现并发

    一.进程池和线程池 当被操作对象数目不大时,我们可以手动创建几个进程和线程,十几个几十个还好,但是如果有上百个上千个.手动操作麻烦而且电脑硬件跟不上,可以会崩溃,此时进程池.线程池的功效就能发挥了.我 ...

  2. 并发编程(六)——进程/线程池、协程、gevent第三方库

    进程/线程池.协程.gevent第三方库 一.进程/线程池 1.进程池 (1)什么是进程池 如果需要创建的子进程数量不大,可以直接利用multiprocess中的Process来创建.但是当需要创建上 ...

  3. python3下multiprocessing、threading和gevent性能对比----暨进程池、线程池和协程池性能对比

    python3下multiprocessing.threading和gevent性能对比----暨进程池.线程池和协程池性能对比   标签: python3 / 线程池 / multiprocessi ...

  4. 并发编程 --进、线程池、协程、IO模型

    内容目录: 1.socket服务端实现并发 2.进程池,线程池 3.协程 4.IO模型 1.socket服务端实现并发 # 客户端: import socket client = socket.soc ...

  5. 进程池与线程池、协程、协程实现TCP服务端并发、IO模型

    进程池与线程池.协程.协程实现TCP服务端并发.IO模型 一.进程池与线程池 1.线程池 ''' 开进程开线程都需要消耗资源,只不过两者比较的情况下线程消耗的资源比较少 在计算机能够承受范围内最大限度 ...

  6. Python 37 进程池与线程池 、 协程

    一:进程池与线程池 提交任务的两种方式: 1.同步调用:提交完一个任务之后,就在原地等待,等任务完完整整地运行完毕拿到结果后,再执行下一行代码,会导致任务是串行执行 2.异步调用:提交完一个任务之后, ...

  7. 并发编程(六)--进程/线程池、协程、gevent第三方库

    一.进程/线程池 1.进程池 (1)什么是进程池 如果需要创建的子进程数量不大,可以直接利用multiprocess中的Process来创建.但是当需要创建上百个或上千个,手动创建就较为繁琐,这时就可 ...

  8. concurrent.futures进线程池和协程

    concurrent.futures 异步执行进程线程池的模块,一个抽象类,定义submit,map,shutdown方法 from concurrent.futures import Process ...

  9. 并发编程中死锁、递归锁、进程/线程池、协程TCP服务器并发等知识点

    1.死锁 定义; 类似两个人分别被囚禁在两间房子里,A手上拿着的是B囚禁房间的钥匙,而B拿着A的钥匙,两个人都没法出去,没法给对方开锁,进而造成死锁现象.具体例子代码如下: # -*-coding:u ...

随机推荐

  1. Unity2D音游案例-节奏大师教程+源码+素材

    Unity2D音游案例-节奏大师(Unity2017.2 ,基于Koreographer),本案例涉及到一些音乐音频音波的一点点内容. 首先,我们会把音游的核心功能实现,之后几个重要内容会出给思路并带 ...

  2. Sql中substr的使用

    pandas和SQL数据分析实战 https://study.163.com/course/courseMain.htm?courseId=1006383008&share=2&sha ...

  3. tomcat高并发优化的参数优化并查看tomcat线程数

    在Tomcat配置文件conf下面 server.xml 中的配置中和连接数相关的参数有: minProcessors:最小空闲连接线程数,用于提高系统处理性能,默认值为10 maxProcessor ...

  4. java中HashSet对象内的元素的hashCode值不能变化

    因为不管是HashMap(或HashTable,还是HashSet),key值是以hashCode值存进去的,加入key值变了,将无法从集合内删除对象,导致内存溢出.

  5. 前端解析 excel docx

    在研究中... https://www.npmjs.com/package/xlsx https://www.jianshu.com/p/68a420a68ded https://www.jiansh ...

  6. numtodsinterval 函数用法

    numtodsinterval(<x>,<c>) ,x是一个数字,c是一个字符串,表明x的单位,这个函数把x转为interval day to second数据类型 常用的单位 ...

  7. Laravel 登录后跳转回登录前浏览的页面

    一.经过 Auth 中间件检查后跳转至登录页面 也就是没有通过 auth 中间件的认证检查,被 auth 中间件拦截后跳转至登录页面.这种情况下,Laravel 默认会在用户登录成功后自动跳转回登录前 ...

  8. dotnet core 3.0 swagger 显示枚举描述

    上一篇net core 2.2 swagger的枚举描述,core 3.0 需要升级swagger到5.0rc版,配置需要做些修改,swaager启用了OpenApi标准,之前的枚举描述方法也失效了. ...

  9. GPRS 智能门禁控制器

    本模块居于GPRS 2G网络,信号覆盖广,而且好. 主要用于微信门禁等,提供用户服务端搭建及相关接口. 您可以向门禁发送开门信号,并提醒开门成功的信号反馈. 同时支持发送开门ID号,并反馈成功ID号

  10. cordova调用第三方应用

    cordova 帮助webapp 达到调用原生系统的功能 项目需求:在项目中调用系统中含有的第三方地图应用 需求其实分为两步: 1. 查找本地地图应用 2.成功调起本地应用 首先需要安装两个插件,安装 ...