线程queue
第一种、先进先出
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 还没有值直接报错
结果:
#1
#2
#3
第二种、后进先出 Lifo 堆栈
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
第三种、优先级队列
q = queue.PriorityQueue(4)
q.put((5, '元宝'))
q.put((-2,'狗狗'))
q.put((0, '李业'))
print(q.get())
print(q.get())
print(q.get())
结果:
(-2, '狗狗')
(0, '李业')
(5, '元宝')
事件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) # 只阻塞1秒,1秒之后如果还没有进行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(4)
event.set()
print('服务器已经开启...')
def connect():
count = 1
while not event.is_set():
if count == 4:
print('连接次数过多,已断开')
break
event.wait(1)
print(f'{current_thread().name} 尝试连接{count}次')
count += 1
else:
print(f'{current_thread().name} 连接成功...')
t1 = Thread(target=check,)
t2 = Thread(target=connect,)
t1.start()
t2.start()
自写版:
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)
event.set()
if t2.is_alive():
print('服务器已经开启')
def connect():
count = 3
while count:
count -= 1
print(f'{current_thread().name} 等待连接')
event.wait(1)
if event.is_set():
print(f'{current_thread().name} 连接成功')
break
else:
print(f'{current_thread().name} 连接失败')
协程
#一个线程并发的处理任务
#串行:一个线程执行一个任务,执行完毕之后,执行下一个任务
#并行:多个CPU执行多个任务,4个CPU执行4个任务
#并发:一个CPU执行多个任务,看起来像是同时执行
并发真正的核心:切换并且保持状态
多线程的并发:3个线程处理10个任务,如果线程1处理的这个任务,遇到阻塞,cpu被操作系统切换到另一个线程,
#1.单个CPU:10个任务,让你给我并发的执行这10个任务
# 方式一:开启多进程并发执行,操作系统+保持状态
# 方式二:开启多线程并发执行,操作系统+保持状态
# 方式三:开启协程并发的执行,自己的程序把控着CPU在多个任务之间来回的切换+保持状态
# 方式三的详细解释:
# 协程切换的速度非常快,蒙蔽操作系统的眼睛,让操作系统认为CPU一直在运行你这个线程(协程)
2.使用协程最好的原因:
1.开销小
2.运行速度快
3.协程会长期霸占CPU只执行我程序里边的所有任务
3.并发的本质:
就是切换+保持状态
#4.协程处理IO密集型,计算密集型还是串行比较好
5.协程的特点:
1.必须在只有一个单线程里实现并发
2.修改共享数据不需加锁
3.用户程序里自己保存多个控制流的上下文栈(保持状态)
4.附加:一个协程遇到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()
切换 + 保持状态
# def gen():
# while 1:
# yield 1
#
# def func():
# obj = gen()
# for i in range(10):
# next(obj)
# func()
上面的例子遇到IO不能自动切换
# import time
# def gen():
# while 1:
# yield 1
# time.sleep(0.5)
#
# def func():
# obj = gen()
# for i in range(10):
# next(obj)
# func()
#
切换 +保持状态(遇到IO不会主动切换)
# from greenlet import greenlet
# import time
# def eat(name):
# print('%s eat 1' %name) # 2
# # g2.switch('taibai') # 3
# time.sleep(3)
# print('%s eat 2' %name) # 6
# g2.switch() # 7
#
# def play(name):
# print('%s play 1' %name) # 4
# g1.switch() # 5
# print('%s play 2' %name) # 8
#
# g1=greenlet(eat)
# g2=greenlet(play)
#
# g1.switch('taibai') # 1 切换到eat任务
#import time
协程
模拟的阻塞,不是真正的阻塞(遇到time.sleep()还是会阻塞)
# import gevent
# from threading import current_thread
# def eat(name):
# print('%s eat 1' %name)
# print(current_thread().name)
# # gevent.sleep(2)
# time.sleep(2)
# print('%s eat 2' %name)
# def play(name):
# print('%s play 1' %name)
# print(current_thread().name)
# # gevent.sleep(1)
# time.sleep(1)
# print('%s play 2' %name)
# g1 = gevent.spawn(eat,'egon')
# g2 = gevent.spawn(play,name='egon')
# print(f'主{current_thread().name}')
# g1.join()
# g2.join()
最终版本(真正的协程):
import gevent
from gevent import monkey
monkey.patch_all() # 打补丁: 将下面的所有的任务的阻塞都打上标记
def eat(name):
print('%s eat 1' %name)
time.sleep(2)
print('%s eat 2' %name)
def play(name):
print('%s play 1' %name)
time.sleep(1)
print('%s play 2' %name)
g1 = gevent.spawn(eat,'egon')
g2 = gevent.spawn(play,name='egon')
# g1.join()
# g2.join()
gevent.joinall([g1,g2])