zoukankan      html  css  js  c++  java
  • 线程队列,协程基础

    线程队列

    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,即假线程

  • 相关阅读:
    Spring Boot启用Swagger2
    Springboot 注解最全详解
    spring-boot-starter-data-jpa 解析
    springboot 微信支付
    springboot整合PageHelper
    SpringBoot配置HTTPS,并实现HTTP访问自动转HTTPS访问
    Springboot 发送短信验证码
    Java volatile关键字的作用
    Android兼容性测试应该怎么做逼格更高呢?
    hadoop日志【2】
  • 原文地址:https://www.cnblogs.com/lucky75/p/11152224.html
Copyright © 2011-2022 走看看