第39篇 threading模块里面的local用法 5种IO模型 IO多路复用 select模块

内容概览:
local的概念
IO模型
IO多路复用

简述进程,线程,协程的区别,以及应用场景

进程:
特点:
进程是计算机中最小的资源分配单位
进程和线程是包含的关系,每个进程至少含有一个线程
可以利用多核,进程之间的数据是隔离的
进程的创建,销毁,以及切换 时间开销比较大
随着开启数量的增加 给操作系统带来负担
场景:
适用于利用多核的,高计算型的,启动数量有限的程序,

线程:
特点:
线程是CPU调度的最小单位,线程的切换是由操作系统完成的
由于GIL锁,在Cpython解释器下不能利用多核,
线程之间数据是共享的
线程的创建,销毁,切换的时间开销比进程要小很多
随着开启数量的增加,也会给操作系统带来负担
场景:
高IO型的,调度是用户不能干预的,程序
部分协程现有的模块不能自动规避IO操作的程序,适合用线程

协程:
特点:
协程的切换是用户自定义的
不能利用多核,不会产生数据不安全,
多个任务之间的切换不依赖与操作系统,
无论开启多少个协程,都不会给操作系统增加负担
应用场景:
高IO型,用户可以自己控制切换的,
能否抢占CPU更多的资源取决于用户的切换策略
适合使用于利用协程模块来规避IO操作的程序

threading模块里面的local用法:
多个线之间使用threading.local对象
obj=threading.local()#在那个线程生成就属于哪个线程,线程与线程之间是隔离的
obj.name = name
obj.age = age
可以实现多个线程之间数据的隔离

import time
import random
from threading import local,Thread

loc = local()
def func2():
    global loc
    print(loc.name,loc.age)

def func(name,age):
    global loc
    loc.name =name
    loc.age = age
    time.sleep(random.random())
    func2()
Thread(target=func,args=('alex',40)).start()
Thread(target=func,args=('bos_jin',38)).start()

  



bos_jin 38
alex 40

  




五种IO模型:
Richard Stevens在书中介绍了5种模型
UNIX® Network Programming Volume 1,
Third Edition: The Sockets Networking ”,6.2节“I/O Models ”,

blocking IO 阻塞IO
noblocking IO 非阻塞IO
IO multiplexing IO多路复用
signal driven IO信号驱动IO
asycchronous 异步IO

同步与异步的区别:
同步:
一键事情做完了再去做另一件事情
异步:
同时做多件事情
相对论 异步是一个相对的概念从宏观上来看,似乎同时完成了几件事情

阻塞:
sleep input join shutdown get acquire wait
accept recv recvrfrom
非阻塞:
setblocking(False)

阻塞的IO会经历两个阶段
数据等待阶段 wait for data
数据拷贝阶段 copy data

用非阻塞IO写一个并发的聊天:

server端:
import time
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8989))
sk.setblocking(False)#非阻塞
sk.listen()

conn_lsit = []
del_list = []

while True:
    try:
        conn,addr = sk.accept()
        conn_lsit.append(conn)
    except BlockingIOError:
        for conn in conn_lsit:
            try :
                conn.send(b'hello')
                print(conn.recv(1024))
            except (NameError,BlockingIOError):pass
            except ConnectionResetError:
                conn.close()
                del_list.append(conn)
        for del_conn in del_list:
            conn_lsit.remove(del_conn)
        del_list.clear()

  



client端:
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',8989))
while True:
    print(sk.recv(1024))
    sk.send(b'bye')

  


总结一下:
非阻塞IO
没有使用并发编程的机制 多进程 线程 进程池 线程池 协程
完全是一个同步的程序实现了非阻塞
整体思路:
程序不会再某一个recv accept上被阻塞
因此程序可以腾出更多的时间来进行其他的收发工作

利弊:
太多的while True循环 以及报错处理
CPU的高速运转 占用了大量的CPU资源 CPU资源被大量的浪费

IO多路复用 select模块
找了一个代理专门从事于消息的收发,来了信息则通知程序来copy数据

server端:
import select #用于对接 操作系统中的 select机制(多路复用)
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8989))
sk.setblocking(False)
sk.listen()

r_lst = [sk,]
while True:
    r_l,_,_ = select.select(r_lst,[],[])#r_lst作为监听的对象
    for item in r_l:
        if item is sk:
            conn,addr = sk.accept()
            r_lst.append(conn)
        else:
            try:
                print(item.recv(1024))
                item.send(b'hello')
            except ConnectionResetError:
                item.close()
                r_lst.remove(item)

  


client:
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',8989))
while True:
    sk.send(b'bye')
    print(sk.recv(1024))

  


io多路复用的总结:

select模块:
运行平台:windows/mac/linux
底层是操作系统的轮询
有监听个数的限制
随着监听个数的增加 效率会降低
poll模块:
运行平台:mac/linux
底层是操作系统的轮询
有监听个数的限制,但是比select监听的个数多
随着监听个数的增加 效率会降低

epoll模块:
给每个监听的对象都绑定一个回调函数
不在受到监听个数的限制 效率不会受到影响

猜你喜欢

转载自www.cnblogs.com/cavalier-chen/p/9715599.html