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. vue-router踩坑日记Unknown custom element router-view

    今天笔者在研究vue-router的时候踩到了一个小坑,这个坑是这样的 笔者的具体代码如下:router.js import Home from '@/components/Home.vue'; im ...

  2. 【C++】C++中的类模板

    基础的类模板 模板类的继承 内部声明定义普通模板函数和友元模板函数 内部声明友元模板函数+外部定义友元模板函数 声明和定义分别在不同的文件(模板函数.模板友元) C++中有一个重要特性,那就是模板类型 ...

  3. C语言 按位异或实现加法

    /* C语言 按位异或实现加法 */ #include <stdio.h> #include <stdlib.h> #include <string.h> void ...

  4. EasyNVR摄像机网页无插件直播方案H5前端构建之:如何区分PC端和移动端

    背景分析 随着互联网基础设施建设的不断完善和发展,带宽的不断提速,尤其是光纤入户,4G/5G/NB-IoT各种网络技术的大规模商用,视频随时随地可看.可控的诉求越来越多,尤其是移动应用技术和前端技术的 ...

  5. Windows删除文件夹下的指定格式文件(递归删除)

    问题描述: 今天遇到一个需求,需要对文件夹进行文件筛选.目录结构较为复杂(目录较多,层次较深),数据量较大(总共60GB左右). 鉴于上述情况,直接排除了人工处理方式(否则小伙伴们会打死我的). 解决 ...

  6. 【linux】查看端口占用情况

    netstat -nlp | grep "端口号"

  7. SQL Server表水平分区

    随着项目的运行时间越来越久,数据库的数据会越来越多,某些表因为数据量的变大查询起来会很慢,而且拥有大量数据的表整张表的数据都存于一个mdf文件中,不利于数据文件的维护和管理,我们一般都会通过优化sql ...

  8. 007 SpringCloud 学习笔记3-----Eureka注册中心

    1.Eureka概述 (1)引子 网约车出现以前,人们出门叫车只能叫出租车.一些私家车想做出租却没有资格,被称为黑车.而很多人想要约车,但是无奈出租车太少,不方便.私家车很多却不敢拦,而且满大街的车, ...

  9. [转帖]Stack Overflow上188万浏览量的提问:Java 到底是值传递还是引用传递?

    Stack Overflow上188万浏览量的提问:Java 到底是值传递还是引用传递? http://www.itpub.net/2019/12/03/4567/   在逛 Stack Overfl ...

  10. was unable to refresh its cache! status = Cannot execute request on any known server

    出现这种错误是因为: Eureka服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为. 在 yml中设置 eureka.client.register-with-eu ...