初学python之路-day38

1.线程回调

回调:
在线程池/进程池
每次提交任务 都会返回一个表示任务的对象 Future对象
Future对象具备一个绑定方法  add_done_callback 用于指定回调函数
add 意味着可以添加多个回调函数

如果直接使用Thread的话 如何完成回调:

from threading import Thread

import time

# res = None


def call_back(res):
    print("任务结果拿到了:%s" % res)

def parser(res):
    print("任务结果拿到了:%s" % res)

def task(callback):
    # global res
    print("run")
    time.sleep(1)
    #     # return 100
    res = 100 # 表示任务结果
    callback(res) # 执行回调函数 并传入任务结果



t = Thread(target=task,args=(parser,))
t.start()
# t.join()
# print(res)
print("over")

2.线程中的队列

from queue import Queue,LifoQueue,PriorityQueue

1.Queue() 先进先出
# 与进程中的Joinablequeue  使用方式一模一样  但是 不具备IPC
# q = Queue()
#
# q.put("123")
# q.put("456")
#
# print(q.get())
# print(q.get())
#
# # print(q.get(block=True,timeout=3))
# q.task_done()
# q.task_done()
# q.join()
# print("over")

2.LifoQueue
last in first out 后进先出   先进 后出   模拟堆栈
# 除顺序以外别的都一样
# lq = LifoQueue()
#
# lq.put("123")
# lq.put("456")
#
# print(lq.get())
# print(lq.get())

3. PriorityQueue 
具备优先级的队列
# 可以存储一个可以比较大小的对象    比较越小的优先级越高
自定义对象 不能使用比较运算符 所以不能存储
q=queue.PriorityQueue() #put进入一个元组,元组的第一个元素是优先级(通常是数字,也可
以是非数字之间的比较),数字越小优先级越高
q.put((20,'a')) q.put((10,'b')) q.put((30,'c')) print(q.get()) print(q.get()) print(q.get()) ''' 结果(数字越小优先级越高,优先级高的优先出队): (10, 'b') (20, 'a') (30, 'c')

3.事件EVENT

事件Event:
事件,表示发生了某件事情,我们可以去关注某个事件然后采取一
些行动 本质上事件是用来线程间通讯的 ,用于状态同步
from threading import Thread,Event import time boot_event = Event() # boot_event.clear() 回复事件的状态为False # boot_event.is_set() 返回事件的状态 # boot_event.wait()等待事件发生 ,就是等待事件被设置
为True
# boot_event.set() 设置事件为True def boot_server(): print("正在启动服务器......") time.sleep(3) print("服务器启动成功!") boot_event.set() # 标记事件已经发生了 def connect_server(): boot_event.wait() # 等待事件发生 print("链接服务器成功!") t1 = Thread(target=boot_server) t1.start() t2 = Thread(target=connect_server) t2.start()

4.协程

协程的目的就是要在单线程中实现并发
并发:
多个任务看起来是同时运行 本质是切换+保存状态 
1.生成器中yiled实现并发效果
# 使用生成器来实现 单线 并发多个任务
import time
# def func1():
#     a = 1
#     for i in range(10000000):
#         a += 1
#         # print("a run")
#         yield
#
# def func2():
#     res = func1()
#     a = 1
#     for i in range(10000000):
#         a += 1
#         # print("b run")
#         next(res)
#
# st = time.time()
# func2()
# print(time.time() - st)

def func1():
    a = 1
    for i in range(10000000):
        a += 1

def func2():
    a = 1
    for i in range(10000000):
        a += 1


st = time.time()
func1()
func2()
print(time.time() - st)
经过测试 单线程并发并不能提高性能 , 对于计算密集型
任务而言 对于IO操作而言 必须具备能够检测io操作 并自动切换其他
区任务 ,这才提高效率
2.greenlet
greenlet需要手动切换,而且不能 检测IO ,是对yie
ld的封装
import greenlet import time def task1(): print("task1 run") g2.switch() print("task1 over") g2.switch() def task2(): print("task2 run") g1.switch() time.sleep(2) print("task2 over") g1 = greenlet.greenlet(task1) g2 = greenlet.greenlet(task2) g1.switch() # g2.switch() print("主 over")
3.gevent
gevent  是协程 ,为轻量级线程 ,也称之为微线程,
是应用程序级别的任务调度方式 

提高效率:
在Cpython中有GIL锁 导致多线程不能并行执行丧失了 多
核优势,即使开启了多线程也只能并发, 这时候完全 可以
使用协程来实现并发 协程的优缺点: 优点:不会占用更多无用的资源 缺点:如果是计算任务 使用协程反而降低效率 IO密集型任务 采用协程

5.猴子补丁

本质就是把原本阻塞的代码 悄悄换成非阻塞代码  

例如 Queue.get(block=false)
当执行get而取不到值时 会抛出异常  只需捕获异常  然后
在发生时切换到其他任务 ,就可以实现遇到IO 切换任务
# gevent 不具备检测IO的能力 需要为它打补丁 打上补
丁之后就能检测IO
# 注意补丁一定打在最上面 必须保证导入模块前就打好补丁 from gevent import monkey monkey.patch_all() # from threading import current_thread import gevent,time def task1(): print(current_thread(),1) print("task1 run") # gevent.sleep(3) time.sleep(3) print("task1 over") def task2(): print(current_thread(),2) print("task2 run") print("task2 over") # spawn 用于创建一个协程任务 g1 = gevent.spawn(task1) g2 = gevent.spawn(task2) # 任务要执行,必须保证主线程没挂 因为所有协程任务
都是主线在执行 ,必须调用join来等待协程任务
# g1.join() # g2.join() # 理论上等待执行时间最长的任务就行 , 但是不清楚
谁的时间长 可以全部join
gevent.joinall([g1,g2]) print("over")
协程: 
在Cpython 多线程无法并行 执行 ,在IO密集任务中 
并且并发量较大,无法开启更多的线程时,造成后续的任
务无法处理,即使前面都在等待IO 就可以使用单线程 实现并发,当某一个任务处于IO阻塞时
,可以切换到其他的任务来执行,可以充分利用CPU时间片
,如果任务量足够,可以占用CPU直到超时 最终的解决方案:多进程
+单线程 + 协程 对于更多的任务: 1.集群 所有服务器干的活都一样 2.分布式 每个服务器就干某个活

猜你喜欢

转载自www.cnblogs.com/wangwei5979/p/10986787.html