协程和断点上传
线程中的队列
我们经常会遇到这样的一个问题,这里有成千上万条数据,每次需要取出其中的一条数据进行处理,那么引入多线程该怎么进行任务分配?
我们可以将数据进行分割然后交给多个线程去跑,可是这并不是一个明智的做法。在这里我们可以使用队列与线程相结合的方式进行任务分配。
队列线程的思想: 首先创建一个全局共享的队列,队列中只存在有限个元素,并将所有的数据逐条加入到队列中,并调用队列的join函数进行等待。之后便可以开启若干线程,线程的任务就是不断的从队列中取数据进行处理就可以了。
from queue import Queue,LifoQueue,PriorityQueue # 与进程中的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") # last in first out 后进先出 先进 后出 模拟堆栈 =========================================================== # LifoQueue # 除顺序以外别的都一样 # lq = LifoQueue() # # lq.put("123") # lq.put("456") # # print(lq.get()) # print(lq.get()) # 具备优先级的队列 # PriorityQueue ================================================================ # 可以存储一个可以比较大小的对象 比较越小的优先级越高 自定义对象 不能使用比较运算符 所以不能存储 class A(object): def __init__(self,age): self.age = age # def __lt__(self, other): # return self.age < other.age # # def __gt__(self, other): # return self.age > other.age def __eq__(self, other): return self.age == other.age a1 = A(50) a2 = A(50) print(a1 == a2) # print(a1 is a1) # pq = PriorityQueue() # pq.put("a") # pq.put("A") # pq.put("C") # # # print(pq.get())
协程:
什么是协程:
是单线程下的并发,又称微线程,纤程。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。
需要强调的是:
1. python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关)
对比操作系统控制线程的切换,用户在单线程内控制协程的切换
1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
2. 单线程内就可以实现并发的效果,最大限度地利用cpu
缺点如下:
1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程来尽可能提高效率
2. 协程本质是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
gevent 中的monkey 补丁是协程中重要的接口,因为在我们使用gevent来实现单线程并发的时候是不能够实现进行io操作,所以就有了monkey这个接口
帮助gevent来实现对io的操作,这里还需要注意的是在补丁的时候是,需要在py文件的商法实现补丁,在文件的下方法无法实现补丁的效果
如何实现单线程并发(协程):
# 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")
断点下载文件:
在讲断点下载文件,需要注意的是几种状态,其他的都是之前学的知识
info = {"filename":True} 或者 info = {"filename":false} 或者 info = {} 在info中为True 就是已完成 为false就是未完成 目前有四种状态 文件存在: ----> 下载完成 文件已存在,且info 中的value 为True 并且文件这个路径也是存在的就是下载完成状态 ----> 下载了文件,但是文件没有被下载完成 文件下载文件未完成,状态是在info中的的value 为false 并且文件的路径也是存在的 文件不存在: ----> 新的任务 文件不存在,这个在value中的filename不存在,就是一个空{}这就记录为是新的任务 ----> 文件被删除了 文件路劲不存在,在info 中的状态是false就是文件已经被删除了