python Event事件、进程池与线程池、协程解析
Event事件
用来控制线程的执行
出现e.wait(),就会把这个线程设置为False,就不能执行这个任务;
只要有一个线程出现e.set(),就会告诉Event对象,把有e.wait的用户全部改为True,剩余的任务就会立马去执行。由一些线程去控制另一些线程,中间通过Event。
fromthreadingimportEvent
fromthreadingimportThread
importtime
#调用Event实例化出对象
e=Event()
#
##若该方法出现在任务中,则为False,阻塞
#e.wait()#False
##若该方法出现在任务中,则将其他线程的False改为True,进入就绪态和运行态
#e.set()#True
deflight():
print('红灯亮...')
time.sleep(5)
#应该发出信号,告诉其他线程准备执行
e.set()#将car中的False变为True
print('绿灯亮...')
defcar(name):
print('正在等红灯...')
#让所有汽车任务进入阻塞态
e.wait()#False
print(f'{name}正在加速飘逸...')
#让一个light线程控制多个car线程
t=Thread(target=light)
t.start()
foriinrange(10):
t=Thread(target=car,args=(f'汽车{i}号',))
t.start()
进程池与线程池
进程池与线程池是用来控制当前程序允许创建(进程/线程)的数量
作用:保证在硬件允许的范围内创建(进程/线程)的数量
线程池使用一:
fromconcurrent.futuresimportThreadPoolExecutor
importtime
pool=ThreadPoolExecutor(5)#5代表只能开启5个进程,不加默认使用cpu的进程数
#ThreadPoolExecutor(5)#5代表只能开启5个线程
#pool.submit()#异步提交任务,括号里传函数地址
deftask():
print('线程任务开始了...')
time.sleep(1)
print('线程任务结束了...')
forlineinrange(5):
pool.submit(task)
使用二:
fromconcurrent.futuresimportThreadPoolExecutor
importtime
pool=ThreadPoolExecutor(5)#5代表只能开启5个进程,不加默认使用cpu的进程数
#ThreadPoolExecutor(5)#5代表只能开启5个线程
#pool.submit()#异步提交任务,括号里传函数地址
deftask():
print('线程任务开始了...')
time.sleep(1)
print('线程任务结束了...')
return123
#回调函数
defcall_back(res):
print(type(res))
res2=res.result()#注意:赋值操作不要与接收的res同名
print(res2)
forlineinrange(5):
pool.submit(task).add_done_callback(call_back)
pool.shutdown()会让所有线程池的任务结束后,才往下执行代码
多线程爬取梨视频
利用requests模块,封装底层socket套接字
- 主页中获取所有视频id号,拼接视频详情页url
- 在视频详情页中获取真实视频urlsrcUrl=
- 往真实视频url地址发送请求获取视频二进制数据
- 最后把视频二进制数据保存到本地
协程
- 进程:资源单位
- 线程:执行单位
- 协程:在单线程下实现并发
注意:协程不是操作系统资源,目的是让单线程实现并发
协程目的
- 操作系统:使用多道技术,切换+保存状态,一个是遇到IO,另一个是CPU执行时间过长
- 协程:通过手动模拟操作系统“多道计数”,实现切换+保存状态
- 手动实现,遇到IO切换,欺骗操作系统误以为没有IO操作
- 单线程时,遇到IO,就切换+保存状态
- 单线程时,对于计算密集型,来回切换+保存状态反而效率更低
优点:在IO密集型的情况下,会提高效率
缺点:若在计算密集型的情况下,来回切换,反而效率更低
importtime deffunc1(): foriinrange(10000000): i+1 deffunc2(): foriinrange(10000000): i+1 start=time.time() func1() func2() stop=time.time() print(stop-start)#1.0312113761901855 #基于yield实现并发在计算密集型的情况下效率更低 deffunc1(): whileTrue: 10000000+1 yield deffunc2(): g=func1() foriinrange(10000000): i+1 next(g)#每次执行next相当于切换到func1下面 start=time.time() func2() stop=time.time() print(stop-start)#1.3294126987457275
gevent
gevent是一个第三方模块,可以帮你监听IO操作,并切换
使用gevent的目的:在单线程下实现,遇到IO就会保存状态+切换
importtime
fromgeventimportmonkey
monkey.patch_all()#可以监听该程序下所有的IO操作
fromgeventimportspawn,joinall#用于做切换+保存状态
deffunc1():
print('1')
time.sleep(1)#IO操作
deffunc2():
print('2')
time.sleep(3)
deffunc3():
print('3')
time.sleep(5)
start=time.time()
s1=spawn(func1)
s2=spawn(func2)
s3=spawn(func3)
s1.join()#发送信号,相当于等待自己(在单线程的情况下)
s2.join()
s3.join()
#joinall((s1,s2,s3))#一个个执行很麻烦,可以用joinall把这些全部装进去
end=time.time()
print(end-start)#5.006161451339722
TCP服务端socket套接字实现协程
服务端:
fromgeventimportmonkey
fromgeventimportspawn
importsocket
monkey.patch_all()
server=socket.socket()
server.bind(('127.0.0.1',9999))
server.listen(5)
deftask(conn):
whileTrue:
try:
data=conn.recv(1024)
iflen(data)==0:
break
print(data.decode('utf-8'))
send_data=data.upper()
conn.send(send_data)
exceptException:
break
conn.close()
defserver2():
whileTrue:
conn,addr=server.accept()
print(addr)
spawn(task,conn)
if__name__=='__main__':
s=spawn(server2)
s.join()
客户端:
importsocket
fromthreadingimportThread,current_thread
defclient():
client=socket.socket()
client.connect(('127.0.0.1',9999))
number=0
whileTrue:
send_data=f'{current_thread().name}{number}'
client.send(send_data.encode('utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))
number+=1
foriinrange(400):
t=Thread(target=client)
t.start()
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。