Python3并发编程篇之非阻塞IO模型

介绍

阻塞IO模型:

当发起类似accept / recv / recvfrom / send等系统调用调用之后,进程并没有被阻塞,
内核马上返回到进程,如果数据还没准备好,此时会返回一个error。
进程在返回之后,可以干点别的事情,然后再次发起系统调用。
重复上面的过程,循环往复的进行accept / recv / recvfrom / send等系统调用调用, 这个过程通常被称之为轮询。
轮询检查内核数据,如果数据准备好了,再将数据拷贝到进程内存中,进行数据处理。
需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态, 只不过非常快而已。

优点

能够在等待任务完成的时间里做其他事情。

缺点

  1. 循环发起系统调用将大幅度推高CPU占用率,(因为没有阻塞,那么程序将最大限度地处于就绪状态)
    在低配主机下极容易出现卡机情况。

  2. 任务完成的响应延迟增大了,因为每过一段时间才去轮询一次拿数据的操作,
    而任务可能在两次轮询之间的任意时间完成。这会导致整体数据吞吐量的降低。

实现

Server端

#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""非阻塞IO模型

Server端

非阻塞IO模型:
    当发起类似accept / recv / recvfrom / send等系统调用调用之后,进程并没有被阻塞,
    内核马上返回到进程,如果数据还没准备好,此时会返回一个error。
    进程在返回之后,可以干点别的事情,然后再次发起系统调用。
    重复上面的过程,循环往复的进行accept / recv / recvfrom / send等系统调用调用, 这个过程通常被称之为轮询。
    轮询检查内核数据,如果数据准备好了,再将数据拷贝到进程内存中,进行数据处理。
    需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态, 只不过非常快而已。

优点:
    能够在等待任务完成的时间里做其他事情。

缺点:
    1. 循环发起系统调用将大幅度推高CPU占用率,(因为没有阻塞,那么程序将最大限度地处于就绪状态)
      在低配主机下极容易出现卡机情况。

     2. 任务完成的响应延迟增大了,因为每过一段时间才去轮询一次拿数据的操作,
        而任务可能在两次轮询之间的任意时间完成。这会导致整体数据吞吐量的降低。
"""

import socket

sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

sk.bind(('', 8080))

sk.listen(32)

sk.setblocking(False)

rlist = list()  # 用于记录conn
wdict = dict()  # 用于记录要send数据的conn

while 1:
    try:  # try代码用于接收conn
        conn, addr = sk.accept()
        rlist.append(conn)
    except BlockingIOError:  # except代码主要用于没有conn请求时,进行收/发消息的任务

        del_rlist = list()  # 记录主动或者异常断开的conn,以便将其移除
        for conn in rlist:  # for代码块用于接收每个conn发过来的数据
            try:  # try代码用于检测recv没有数据或者conn主动/异常断开后,去干其他的任务
                recvs = conn.recv(1024)
                if not recvs:
                    conn.close()
                    del_rlist.append(conn)  # 类Unix下,异常断开,记录于del_rlist
                    continue  # 下一个conn
                print(recvs.decode(encoding='utf-8', errors='strict'))  # 正常的conn,打印消息
                wdict[conn] = recvs + b'_suffix'  # 如果有消息回复,不直接在这里回复,记录到一个字典中
            except BlockingIOError:   # 表明当前conn的recv没有数据,继续检测下一次conn
                continue
            except ConnectionResetError:  # Windows下, 表明conn异常断开
                conn.close()  # 关闭conn
                del_rlist.append(conn)  # 记录于del_list

        del_wlist = list()  # 当消息发送完成后,记录conn,用于移除conn的send数据
        for conn in wdict:
            try:
                conn.sendall(wdict.get(conn))
                del_wlist.append(conn)
            except BlockingIOError:
                continue

        for conn in del_rlist:  # 移除异常的conn
            rlist.remove(conn)

        for conn in del_wlist:  # 移除conn的send数据
            wdict.pop(conn)

Client端

#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""非阻塞IO模型

Client端
"""

import socket
import threading


def my_client():
    sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
    sk.connect(('localhost', 8080))
    while 1:
        sk.send(b'hi')
        msg = sk.recv(1024)
        print(msg)


for i in range(10):
    threading.Thread(target=my_client).start()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值