day38——线程queue、事件event、协程
day38
线程queue
多线程抢占资源
只能让其串行——用到互斥锁
线程queue
- 队列——先进先出(FIFO)
import queue
q = queue.Queue(3)
q.put(1)
q.put(2)
q.put(3)
# q.put(4) # 阻塞等其他进程或者线程来拿
print(q.get())
print(q.get())
print(q.get())
# print(q.get(block=False)) # 没有值就直接报错
# q.get(timeout=2) # 阻塞2s,还没有值直接报错
- 堆栈——先进后出(LIFO)

import queue
q = queue.LifoQueue(4)
q.put(1)
q.put(2)
q.put("alex")
q.put("太白")
print(q.get())
print(q.get())
print(q.get())
print(q.get())
结果:
太白
alex
2
1
- 优先级队列——自己设置优先级
import queue
q = queue.PriorityQueue(4)
q.put((5, "元宝"))
q.put((-2, "狗狗"))
q.put((0, "2李业"))
q.put((0, "1刚哥"))
print(q.get())
print(q.get())
print(q.get()) # 数字越小就先出去,相同数字按照asill码来排序
事件event
开启两个线程,一个线程运行到中间的某个阶段,触发另个线程执行,两个线程增加了耦合性
版本一
from threading import Thread
from threading import current_thread
import time
flag = False
def check():
print(f"{current_thread().name} 监测服务器是否开启。。。")
time.sleep(3)
global flag
flag = True
print("服务器已经开启。。。")
def connect():
while 1:
print(f"{current_thread().name} 等待连接。。。")
time.sleep(0.5)
if flag:
print(f"{current_thread().name} 连接成功。。。")
break
t1 = Thread(target=check)
t2 = Thread(target=connect)
t1.start()
t2.start()
版本二——事件event
from threading import Thread
from threading import current_thread
from threading import Event
import time
event = Event()
def check():
print(f"{current_thread().name} 监测服务器是否开启。。。")
time.sleep(3)
# print(event.is_set())
event.set()
# print(event.is_set())
print("服务器已经开启。。。")
def connect():
print(f"{current_thread().name} 等待连接。。。")
event.wait() # 阻塞直到event.set() 方法之后
# event.wait(1) # 只阻塞1s,1s之后如果还没有进行set 直接进行下一步操作
print(f"{current_thread().name} 连接成功。。。")
t1 = Thread(target=check)
t2 = Thread(target=connect)
t1.start()
t2.start()
要求:一个线程监测服务器是否开始,另一个线程判断如果开始了,则显示连接成功,此线程只尝试连接3次,1s一次,如果超过3次,还没有连接成功,则显示连接失败
from threading import Thread
from threading import current_thread
from threading import Event
import time
event = Event()
def check():
print(f"{current_thread().name} 监测服务器是否开启")
time.sleep(2)
event.set()
def connect():
print(f"{current_thread().name} 等待连接,,,")
for i in range(3):
event.wait(1)
if event.is_set():
print("服务器已经开启")
print(f"{current_thread().name} 连接成功")
break
else:
print(f"{current_thread().name} 连接失败{i+1}次")
t1 = Thread(target=check)
t2 = Thread(target=connect)
t1.start()
t2.start()
协程
协程详情:https://www.cnblogs.com/jin-xin/articles/11245654.html
一个线程并发的处理任务
- 串行:一个线程执行一个任务,执行完毕之后,执行下一个任务
- 并行:多个CPU执行多个任务,4个CPU执行4个任务
- 并发:一个CPU执行多个任务,看起来像是同时运行
并发真正的核心:切换CPU+保持状态
多线程的并发:3个线程处理10个任务,如果线程1处理的这个任务遇到阻塞,CPU被操作系统切换到另一个线程

一个线程能否并发的处理任务??
一个线程处理三个任务

单个CPU:10个任务,让你给我并发执行这10个任务:
- 方式一:开启多进程并发执行,操作系统切换+保持状态
- 方式二:开启多线程并发执行,操作系统切换+保持状态
- 方式三:开启协程并发的执行,自己的程序把控着CPU在3个任务之间来回切换+保持状态
对3详细解释:协程他切换速度非常快,蒙蔽操作系统的眼睛,让操作系统认为CPU一直再运行你这个线程(协程)
协程方式最好,为什么?
优点:
- 开销小
- 运行速度快
- 协程会长期霸占CPU只执行我程序里面的所有任务
缺点:
协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
协程处理IO密集型好,计算密集型还是串行好
什么是协程?
单个线程并发的处理多个任务,程序控制协程的切换+保持状态
协程的特点
- 必须在只有一个单线程里实现并发
- 修改共享数据不需加锁
- 用户程序里自己保存多个控制流的上下文栈(保持状态)
- 一个协程遇到IO会自动切换到其他任务
工作中
一般在工作中我们都是进程+线程+协程的方式来实现并发,以达到最好的并发效果,如果是4核的cpu,一般起5个进程,每个进程中20个线程(5倍cpu数量),每个线程可以起500个协程,大规模爬取页面的时候,等待网络延迟的时间的时候,我们就可以用协程去实现并发。 并发数量 = 5 * 20 * 500 = 50000个并发,这是一般一个4cpu的机器最大的并发数。nginx在负载均衡的时候最大承载量就是5w个单线程里的这20个任务的代码通常会既有计算操作又有阻塞操作,我们完全可以在执行任务1时遇到阻塞,就利用阻塞的时间去执行任务2。。。。如此,才能提高效率,这就用到了Gevent模块。
之前学的代码有没有切换:
def func1():
print("in func1")
def func2():
print("in func2")
func1()
print("end")
func2()
切换 + 保持状态:遇到IO不会主动切换
def gen():
while 1:
yield 1
print(333)
def func():
obj = gen()
for i in range(10):
print(next(obj))
func()
greenlet——协程底层技术
from greenlet import greenlet
import time
def eat(name):
print(f"{name} eat 1") # 2
g2.switch("taibai") # 3
# time.sleep(3)
print(f"{name} eat 2") # 6
g2.switch() # 7
def play(name):
print(f"{name} play 1") # 4
g1.switch() # 5
print(f"{name} play 2") # 8
g1 = greenlet(eat)
g2 = greenlet(play)
g1.switch("taibai") # 切换到eat任务 1
协程low版
- 模拟的阻塞
import gevent
import time
from threading import current_thread
def eat(name):
print(f"{name} eat 1") # 2
print(current_thread().name) # 3
gevent.sleep(2)
# time.sleep(2)
print(f"{name} eat 2") # 7
def play(name):
print(f"{name} play 1") # 4
print(current_thread().name) # 5
gevent.sleep(1)
# time.sleep(1)
print(f"{name} play 2") # 6
g1 = gevent.spawn(eat, "egon")
g2 = gevent.spawn(play, "egon")
print(f"主{current_thread().name}") # 1
g1.join()
g2.join()
结果:
主MainThread
egon eat 1
MainThread
egon play 1
MainThread
egon play 2
egon eat 2
- 真正的阻塞
import gevent
import time
from threading import current_thread
def eat(name):
print(f"{name} eat 1") # 2
print(current_thread().name) # 3
# gevent.sleep(2)
time.sleep(2)
print(f"{name} eat 2") # 4
def play(name):
print(f"{name} play 1") # 5
print(current_thread().name) # 6
# gevent.sleep(1)
time.sleep(1)
print(f"{name} play 2") # 7
g1 = gevent.spawn(eat, "egon")
g2 = gevent.spawn(play, "egon")
print(f"主{current_thread().name}") # 1
g1.join()
g2.join()
结果:
主MainThread
egon eat 1
MainThread
egon eat 2
egon play 1
MainThread
egon play 2
最终版本
import gevent
import time
from gevent import monkey
monkey.patch_all() # 打补丁:将下面所有任务的阻塞都打上标记
def eat(name):
print(f"{name} eat 1") # 1
time.sleep(2)
print(f"{name} eat 2") # 4
def play(name):
print(f"{name} play 1") # 2
time.sleep(1)
print(f"{name} play 2") # 3
g1 = gevent.spawn(eat, "egon")
g2 = gevent.spawn(play, "egon")
# g1.join()
# g2.join()
gevent.joinall([g1, g2])
结果:
egon eat 1
egon play 1
egon play 2
egon eat 2
协程的应用
爬虫
from gevent import monkey;monkey.patch_all()
import gevent
import requests
import time
def get_page(url):
print('GET: %s' %url)
response=requests.get(url)
if response.status_code == 200:
print('%d bytes received from %s' %(len(response.text),url))
start_time=time.time()
gevent.joinall([
gevent.spawn(get_page,'https://www.python.org/'),
gevent.spawn(get_page,'https://www.yahoo.com/'),
gevent.spawn(get_page,'https://github.com/'),
])
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))
结果:
GET: https://www.python.org/
GET: https://www.yahoo.com/
GET: https://github.com/
48919 bytes received from https://www.python.org/
87845 bytes received from https://github.com/
515896 bytes received from https://www.yahoo.com/
run time is 2.729017734527588
通过gevent实现单线程下的socket并发(from gevent import monkey;monkey.patch_all()一定要放到导入socket模块之前,否则gevent无法识别socket的阻塞)

一个网络请求里面经过多个时间延迟time
server
from gevent import monkey;monkey.patch_all()
from socket import *
import gevent
#如果不想用money.patch_all()打补丁,可以用gevent自带的socket
# from gevent import socket
# s=socket.socket()
def server(server_ip,port):
s=socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind((server_ip,port))
s.listen(5)
while True:
conn,addr=s.accept()
gevent.spawn(talk,conn,addr)
def talk(conn,addr):
try:
while True:
res=conn.recv(1024)
print('client %s:%s msg: %s' %(addr[0],addr[1],res))
conn.send(res.upper())
except Exception as e:
print(e)
finally:
conn.close()
if __name__ == '__main__':
server('127.0.0.1',8080)
client
from socket import *
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
if not msg:continue
client.send(msg.encode('utf-8'))
msg=client.recv(1024)
或多线程并发多个客户端,去请求上面的服务端是没问题的
from threading import Thread
from socket import *
import threading
def client(server_ip,port):
c=socket(AF_INET,SOCK_STREAM) #套接字对象一定要加到函数内,即局部名称空间内,放在函数外则被所有线程共享,则大家公用一个套接字对象,那么客户端端口永远一样了
c.connect((server_ip,port))
count=0
while True:
c.send(('%s say hello %s' %(threading.current_thread().getName(),count)).encode('utf-8'))
msg=c.recv(1024)
print(msg.decode('utf-8'))
count+=1
if __name__ == '__main__':
for i in range(500):
t=Thread(target=client,args=('127.0.0.1',8080))
t.start()
协程的另外一个模块asyncio
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# import asyncio
# 起一个任务.
# async def demo(): # 协程方法
# print('start')
# await asyncio.sleep(1) # 阻塞
# print('end')
# loop = asyncio.get_event_loop() # 创建一个事件循环
# loop.run_until_complete(demo()) # 把demo任务丢到事件循环中去执行
# 启动多个任务,并且没有返回值
# async def demo(): # 协程方法
# print('start')
# await asyncio.sleep(1) # 阻塞
# print('end')
#
# loop = asyncio.get_event_loop() # 创建一个事件循环
# wait_obj = asyncio.wait([demo(),demo(),demo()])
# loop.run_until_complete(wait_obj)
# 启动多个任务并且有返回值
# async def demo(): # 协程方法
# print('start')
# await asyncio.sleep(1) # 阻塞
# print('end')
# return 123
#
# loop = asyncio.get_event_loop()
# t1 = loop.create_task(demo())
# t2 = loop.create_task(demo())
# tasks = [t1,t2]
# wait_obj = asyncio.wait([t1,t2])
# loop.run_until_complete(wait_obj)
# for t in tasks:
# print(t.result())
# 谁先回来先取谁的结果
# import asyncio
# async def demo(i): # 协程方法
# print('start')
# await asyncio.sleep(10-i) # 阻塞
# print('end')
# return i,123
#
# async def main():
# task_l = []
# for i in range(10):
# task = asyncio.ensure_future(demo(i))
# task_l.append(task)
# for ret in asyncio.as_completed(task_l):
# res = await ret
# print(res)
#
# loop = asyncio.get_event_loop()
# loop.run_until_complete(main())
# import asyncio
#
# async def get_url():
# reader,writer = await asyncio.open_connection('www.baidu.com',80)
# writer.write(b'GET / HTTP/1.1\r\nHOST:www.baidu.com\r\nConnection:close\r\n\r\n')
# all_lines = []
# async for line in reader:
# data = line.decode()
# all_lines.append(data)
# html = '\n'.join(all_lines)
# return html
#
# async def main():
# tasks = []
# for url in range(20):
# tasks.append(asyncio.ensure_future(get_url()))
# for res in asyncio.as_completed(tasks):
# result = await res
# print(result)
#
#
# if __name__ == '__main__':
# loop = asyncio.get_event_loop()
# loop.run_until_complete(main()) # 处理一个任务
# python原生的底层的协程模块
# 爬虫 webserver框架
# 题高网络编程的效率和并发效果
# 语法
# await 阻塞 协程函数这里要切换出去,还能保证一会儿再切回来
# await 必须写在async函数里,async函数是协程函数
# loop 事件循环
# 所有的协程的执行 调度 都离不开这个loop
day38——线程queue、事件event、协程的更多相关文章
- 并发编程~~~多线程~~~线程queue, 事件event,
一 线程queue 多线程抢占资源,只能让其串行. 互斥锁 队列 import queue q = queue.Queue() # 先进先出 q = queue.LifoQueue() # 先进后出 ...
- Python自动化运维之16、线程、进程、协程、queue队列
一.线程 1.什么是线程 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位. 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行 ...
- Event事件与协程
1.Event事件 Event事件的作用: - 用来控制线程的执行. - 由一些线程去控制另一些线程. 2.进程池与线程池 1)什么是进程池与线程池? 进程池与线程池是用来控制当前程序允许创建(进程/ ...
- python并发编程之Queue线程、进程、协程通信(五)
单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...
- Python之线程、进程和协程
python之线程.进程和协程 目录: 引言 一.线程 1.1 普通的多线程 1.2 自定义线程类 1.3 线程锁 1.3.1 未使用锁 1.3.2 普通锁Lock和RLock 1.3.3 信号量(S ...
- python运维开发(十一)----线程、进程、协程
内容目录: 线程 基本使用 线程锁 自定义线程池 进程 基本使用 进程锁 进程数据共享 进程池 协程 线程 线程使用的两种方式,一种为我们直接调用thread模块上的方法,另一种我们自定义方式 方式一 ...
- python之路: 线程、进程和协程
进程和线程 既然看到这一章,那么你肯定知道现在的系统都是支持“多任务”的操作,比如: Mac OS X,UNIX,Linux,Windows等. 多任务:简单地说就是同时运行多个任务.譬如:你可以一边 ...
- python第四课——线程、进程、协程
面试or笔试题:简述线程.进程.协程之间的关系? 内容概要 1.进程与线程优.缺点的比较 2.适用情况 3.线程 线程的创建 setDaemon join event RLock 队列 4.进程 创建 ...
- python基础-第九篇-9.1初了解Python线程、进程、协程
了解相关概念之前,我们先来看一张图 进程: 优点:同时利用多个cpu,能够同时进行多个操作 缺点:耗费资源(重新开辟内存空间) 线程: 优点:共享内存,IO操作时候,创造并发操作 缺点:抢占资源 通过 ...
随机推荐
- 洛谷 P1717 钓鱼 题解
每日一题 day46 打卡 Analysis 首先通过题目我们不难发现,为了得到最优解,那么就不能把时间浪费在路上,也就是说不能走回头路.然后很容易可以发现,在每个时刻在不同的鱼塘钓到的鱼的数量是不同 ...
- 如何调试 Windows 服务
概要 本文分步介绍了如何使用 WinDbg 调试程序 (windbg.exe) 调试 Windows 服务. 要调试 Windows 服务,可以在服务启动后将 WinDbg 调试程序附加到托管该服务的 ...
- Xamarin安装及调试
Xamarin介绍 Xamarin是一个跨平台的开发框架(工具集),创始于2011年,旨在使移动开发变得难以置信地迅捷和简单,它是跨平台的,它允许开发人员有效创建可跨 iOS.Android.Wind ...
- PowerDesigner 创建表的时候 没有自增长Id的设置项
今天早上同事创建表的时候,在那个界面没有自增长Id的选项,当时我也纳闷,软件肯定都是一样的,设置的步骤都一样(有些配置好的 我就没改过 然后就忘了还改过些什么步骤了),结果还是没有那个选项 百度了一下 ...
- vue echarts 给双饼图添加点击事件
在用 echarts 画旭双饼图( https://www.echartsjs.com/examples/zh/editor.html?c=pie-nest )的时候,经常会伴随着点击事件 如果想要在 ...
- JDK源码解析---HashMap源码解析
HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的,只是 ...
- 【POJ1068】Parencodings
题目传送门 本题知识点:模拟 这是一道恐怖的括号题.题意稍微理解以下还是可以的. 我们针对样例来理解一下 S.P.W 到底是什么意思: S:( ( ( ( ) ( ) ( ) ) ) ) P: \(P ...
- 在安卓手机下按钮会悬浮在键盘上,怎么解决vue.js
data里面 screenHeight: window.innerHeight mounted里面 mounted () { var that = this var u = navigator.use ...
- D3.js的v5版本入门教程(第四章)—— 理解Update、Enter、Exit
D3.js的v5版本入门教程(第四章) Update.Enter.Exit是D3.js中很重要的概念,下面来讲一下它们到底是什么?(当你看完后.你就会知道如果数据集个数和选择集个数不匹配的情况下使用d ...
- MySQL服务问题
Mysql使用命令 net start mysql net stop mysql 出现如下报错 经过查询得知可能是安装时修改过服务名称 查看服务名称的方法:这台电脑右键->管理->服务和应 ...