• 20.selector/线程回顾


    selectors 模块

    import selectors
    # 基于select模块实现的IO多路复用,建议大家使用
    import socket
    
    sock = socket.socket()
    sock.bind(('127.0.0.1', 8080))
    
    sock.listen(5)
    sock.setblocking(False)
    
    sel = selectors.DefaultSelector()
    # 根据具体平台选择最佳IO多路机制,比如在linux上会优先选择epoll
    # print(sel)
    
    
    def read(conn, mask):
        try:
            data = conn.recv(1024)
            print(data.decode('utf-8'))
            conn.send(data.upper())
        except Exception:
            sel.unregister(conn)
    
    
    def accept(sock, mask):
        conn, addr = sock.accept()
        print(conn)
        sel.register(conn, selectors.EVENT_READ, read)
    
    
    # 注册,传入socket对象,第三个参数为运行的函数
    sel.register(sock, selectors.EVENT_READ, accept)
    # 将sock和accept绑定到sel当中去
    
    
    while 1:
        # 监听
        print('waiting data….')
        event = sel.select()  # 获取活动对象[(key,mask)]
    
        for key, mask in event:
            # print(key.data)  # accept 函数
            # print(key.fileobj)  # 活动的soc对象
    
            func = key.data
            obj = key.fileobj
    
            func(obj, mask)

    IO多路服用实现机制

    windows下 select
    linux 下除了select之外还有poll 和epoll
    select缺点

    1. 每次调用select都需要把所有的文件描述符(fd)拷贝到内核空间,导致效率下降
    2. 遍历所有的fd,是否有数据访问
    3. 最大连接数是有阈值的

    poll:
    最大连接数,没有限制

    epoll:通过三个函数来实现
    第一个函数:创建epoll句柄:将所有的fd拷贝到内核空间,但是只需要拷贝一次;
    第二个函数:回调函数:某一个事件(函数/动作)结束的时候,会自动调用的函数。为所有的fd绑定一个回调函数,一旦有数据访问,触发该回调函数,回调函数将fd放到链表中;

    队列(queue)

    队列在多线程编程,多个线程安全交换数据的情况下尤其有用。

    import queue
    q = queue.Queue()   #可以传入参数最大传入值
    q.put(111)
    q.put('hello')
    q.put('super')
    q.put('test',False)  #如果取值只有三个,第四个数据进入的时候也会默认blocking,这时候添加False就会直接报错,而不是阻塞在这里
    ret1 = q.get()
    ret2 = q.get()
    ret3 = q.get()
    
    ret4 = q.get(True)    #取值取完之后仍然想要取值,这时候会默认blocking(阻塞),在get中传入True后就会不阻塞,直接返回错误值empty
    
    

    优点:线程安全

    join和task_done方法

    q = queue.Queue(5)
    q.put(111)
    q.put(333)
    
    a = q.get()
    b = q.get()
    print(a,b)
    q.task_done()
    q.task_done()
    #有几个操作就要有几个task_done,虽然这里只有一个print,但是task_done和print这种操作无关,只和队列相关的操作相关!
    q.join()
    print('over')

    先进后出

    queue.LifoQueue()    # lifo    last in first out
    q.put(11)
    q.put(22)
    q.put(333)
    print(q.get())
    
    ## 优先级
    q = queue.PriortyQueue()
    q.put([1,'hello1'])
    q.put([2,'hello2'])
    q.put([3,'hello3'])
    q.put([4,'hello5'])
    q.put([5,'hello4'])
    
    while not q.empty:
        q.get()
    

    生产者消费者模型

    回顾

    线程与进程

    为什么需要: 实现并发(切换----由操作系统实现)

    进程:是最小的资源管理单位(可以理解为一个存放线程的容器,一个py程序里,最少有一个线程,就是主线程)
    线程:是最小的执行单位

    并发:同一时间只有一个线程在执行,但是分布式执行线程,可以让某个线程执行一段时间后切换给另外一个线程执行,交替进行。但是由于需要保存各个线程的执行状态,会有消耗
    并行:多个任务同一时刻都在执行,调用了多核cpu
    串行:同一时间只有一个线程在执行,执行完才进行下一个线程

    多进程和多线程都可以执行多个任务,线程是最小的执行单位进程是最小的资源单位,线程跑在进程中,一个进程中一定有一个线程。
    线程的特点是线程之间可以共享内存和变量,资源消耗少(在Unix环境中,多进程和多线程资源调度小号差距不明显,Unix调度较快),缺点是线程之间的同步和枷锁比较麻烦。

    GIL锁:

    cpython的解释器因为存在GIL,导致:同一时刻,同一进程,只能有一个线程被执行

    Tread

    如何创建

    jion方法和setDaemon方法

    daemon:程序直到不存在非守护线程时退出
    是不是可以理解为,当程序只剩下主线程和守护线程的时候就退出

    同步锁

    由于多线程处理公共数据
    存在一个全局变量,所有的线程都可以调用操作这个变量。由于GIL锁,同一时间同一进程只有一个线程在执行。通过sleep模拟一个IO操作,由于线程1在执行IO操作,这时候切换到线程2,线程1的状态被保存。此时由于线程1在执行操作前出现IO操作,并没有来得及对数据进行操作,所以线程2获得的数据仍然是原来的全局变量。

    递归锁和死锁

  • 相关阅读:
    约数的问题
    广度搜索基本逻辑
    奇葩概念
    一枚前端UI组件库 KUI for React
    一枚前端UI组件库 KUI for Vue
    跨域的常见问题和解决方案
    Comet,SSE,WebSocket前后端的实现
    web渐进式应用PWA
    IIS 部署node
    javascript 时间戳
  • 原文地址:https://www.cnblogs.com/scott-lv/p/7487294.html
Copyright © 2020-2023  润新知