day38,线程队列,协程

                                                                              协程和断点上传

线程中的队列

我们经常会遇到这样的一个问题,这里有成千上万条数据,每次需要取出其中的一条数据进行处理,那么引入多线程该怎么进行任务分配?

我们可以将数据进行分割然后交给多个线程去跑,可是这并不是一个明智的做法。在这里我们可以使用队列与线程相结合的方式进行任务分配。

队列线程的思想: 首先创建一个全局共享的队列,队列中只存在有限个元素,并将所有的数据逐条加入到队列中,并调用队列的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就是文件已经被删除了 
上一篇:python-48-协程


下一篇:python – 如何检测套接字断开连接? /如何在超时时调用socket.recv?