day54 线程队列,协程基础

目录

线程队列

Queue

  • 普通的容器,不具备IPC的能力
  • 用法和进程类似,有join方法,原理等同于joinableQueue

LifoQueue

  • 后进先出,用来模拟栈。
  • 与Queue的区别仅是顺序不同

PriorityQueue

  • 具备优先级的队列,取出数据时,越小的优先级越高

运算符重载

  • 使得对象可以被比较
  • __lt__:小于,__gt__:大于
class Person:
    def __init__(self, age, name):
        self.age = age
        self.name = name

    # 覆盖比较运算符,当在两个对象之间使用比较运算符时,会自动触发
    def __lt__(self, other):
        if self.age == other.age:
            return self.name < other.name
        return self.age < other.age


p1 = Person(17, 'haha')
p2 = Person(19, 'lala')
print(p1 < p2)

协程

GIL 导致Cpython中多线程无法并发执行,只能并发执行,效率低

由于GIL锁在多个线程间只能切换执行,创建以及销毁线程以及线程切换需要消耗资源。

并且最主要的问题,多线程容易出现假死问题。

例如:TCP服务器限制最大线程数量为1000,如果这1000个客户有一部分没有进行任务的操作,而新任务就无法被处理,即使CPU是空闲的。

办法:让单个线程负责处理所有任务,从而避免重复创建销毁,以及最大数量限制的问题

单线程实现并发处理

首先并发的原理是多道技术,原理也就是切换+保存状态

任务也就是一个函数,那能否在多个函数之间切换?

使用yield就可以实现,在多个函数之间切换执行

def func1():
    for i in range(20):
        print(1)
        yield

def func2():
    for i in range(20):
        f1.__next__()
        print(2)

f1 = func1()
f2 = func2()

这样做虽然实现了并发,但是效率反而大大降低了,并且遇到IO操作时,会停下而不会并发。

并且使用生成器来处理多任务的话,代码结构会很混乱

Greenlet模块

第三方的模块,主要就封装了生成器,可以使得在多个函数之间切换更为方便

这个模块也仅仅只是封装了生成器让语法更简单,并没有实现自动检测IO操作自动切换

import greenlet

def func1():
    for i in range(3):
        print(1)
        g2.switch()

def func2():
    for i in range(3):
        print(2)
        g1.switch()


g1 = greenlet.greenlet(func1)
g2 = greenlet.greenlet(func2)

g2.switch()

Gevent

在Greenlet的基础上又进行了一次封装

  • 仔细看代码,里面有详细的注释
from gevent import monkey
monkey.patch_all(ssl=False) # 这里就相当于集成了各种阻塞的模块,然后进行一些修改,True就是修改,我的ssl报错,所以我关了,记得在使用模块前打补丁(不是调用模块前)
import time,gevent

def func1():
    print('func1 start')
    time.sleep(3)
    print('func1 over')

def func2():
    print('func2 start')
    print('func2 over')

# spawn用来提交任务
# 写成任务都是以异步方式提交
g1 = gevent.spawn(func1)    # 有参数直接往func1后面填写就可以
g2 = gevent.spawn(func2)

# 因为主程序结束,这两个函数也会结束,所以要保证主程序存活
g1.join()  # 主程序会阻塞在这里,等待g1结束。
gevent.joinall((g1, g2))    # 主程序等待g1,g2结束

# 想要让那两个任务执行,不光主程序不能挂,而且还要出现阻塞的状态,不然也没用
# while True:   # 这样做主程序也不会挂,但是那两个任务开不起来
    # pass

# whilt True:   # 这样做可以
    # time.sleep(0.2)
    
def func3():
    while True:
        time.sleep(0.2)
        
g3 = gevent.spawn(func3)
g3.join()   # 这样封装起来也可以

g1.value    # 这样可以拿到g1的返回值

协程的定义

协程是什么,本质就是在单个线程下实现并发效果,即在多个任务之间做切换

协程又称之为,微线程,是由应用程序自己来控制切换的任务处理方式。

Python中线程是本地线程,就是操作系统级别的线程,是由操作系统来控制调度的。

协程的优势:协程对比线程更加轻量化
1. 开销更小,如果我们能在当前任务遇到IO时切换其他任务,就可以尽可能多利用CPU如果你的任务足够多,就可以将CPU利用直到超时为止。
2.  避免了线程数量上限的问题

协程的缺点:
1. 无法利用多核CPU的优势。

什么时候用协程

IO密集型任务,且任务数量非常大

注意事项:

  1. 协程任务都是以异步方式提交,主线程依旧会继续往下执行,而一旦执行完最后一行,主线程也就结束了。所以还有任务要执行,需要保持主线程存活,并且要有阻塞的状态。
  2. monkey补丁的原理是把原始的阻塞方法替换为修改后的非阻塞方法,来实现IO自动切换。

我们可以用threading.current_thread().getName()来查看每个g1和g2,查看的结果为DummyThread-n,即假线程

上一篇:08 函数基础2


下一篇:09 函数