您好,欢迎来到刀刀网。
搜索
您的当前位置:首页同步 异步 阻塞和非阻塞

同步 异步 阻塞和非阻塞

来源:刀刀网

基本事实:

1.cpu的速度远高于io速度

2.IO包括网络访问和本地文件访问。比如requests,urllib等传统的网络库都是同步的IO

3.网络IO大部分的时间都是处于等待的状态,在等待的时候,cpu是空闲的,但是又不能执行其他的操作

阻塞是指调用函数时候当前线程被挂起。

非阻塞是指调用函数时候当前线程不会被挂起,而是立即返回。

同步和异步是逻辑层和业务层面的叫法,阻塞和非阻塞是在调用函数时当前线程的状态。

阻塞方式发起请求

方法一:

import requests
html = requests.get("http://www.baidu.com").text
# 三次握手 建立tcp连接
# 等待服务器响应
print(html)

 

方法二:
通过socket直接获取html,绕过requests;requests底层是实现的http协议,是阻塞式的IO;socket有非阻赛的方法。

下面代码中是阻塞式的写法

# 阻塞的方式写
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = "www.baidu.com"
client.connect((host, 80))  # 阻塞IO
client.send(
    "GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format(
        "/", host).encode("utf8")
)
data = b""
while 1:
    d = client.recv(1024)  # 阻塞到有数据
    if d:
        data += d
    else:
        break
data = data.decode("utf8")
print(data)

非阻塞方式发起请求

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.setblocking(False)  # 设置为非阻塞的方法

host = "www.baidu.com"
try:
    client.connect((host, 80))
except BlockingIOError as e:
    # 三次握手的过程中 可以做一些其他的事情 不去等待握手成功
    # client.connect((host2, 80))
    pass

while 1:
    try:
        client.send(
            "GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format(
                "/", host).encode("utf8")
        )
        print('send success')
        break
    except OSError as e:
        print('try again')
        pass
data = b""
while 1:
    try:
        d = client.recv(1024)  # 阻塞到有数据
    except BlockingIOError as e:
        continue
    if d:
        data += d
    else:
        break
data = data.decode("utf8")
print(data)

非阻塞方式改进

采用事件循环+回调

import socket
from selectors import DefaultSelector, EVENT_WRITE, EVENT_READ

selector = DefaultSelector()  # 会自动判断操作系统决定用select 还是epoll


# 利用回调 事件循环
class Fetcher:
    def connected(self, key):
        selector.unregister(key.fd)
        self.client.send(
            "GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format(
                "/", self.host).encode("utf8")
        )
        selector.register(self.client.fileno(), EVENT_READ, self.readable)

    def readable(self, key):
        d = self.client.recv(1024)
        if d:
            self.data += d
        else:
            selector.unregister(key.fd)
            data = self.data.decode("utf8")
            print(data)

    def get_url(self, url):
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.client.setblocking(False)  # 设置为非阻塞的方法
        self.data = b""
        self.host = "www.baidu.com"
        try:
            self.client.connect((self.host, 80))
        except BlockingIOError as e:
            # 三次握手的过程中 可以做一些其他的事情 不去等待握手成功
            # client.connect((host2, 80))
            pass
        # 向select中注册事件
        selector.register(self.client.fileno(), EVENT_WRITE, self.connected)


# 核心:事件循环 一个线程完成
def loop_forever():
    while 1:
        ready = selector.select()  # 得到可以操作的socket,得到可操作性的队列
        for key, mask in ready:
            call_back = key.data
            call_back(key)


if __name__ == '__main__':
    fetcher = Fetcher()
    url = "http://www.baidu.com"
    fetcher.get_url(url)
    loop_forever()

面临的问题:

1:回调过深 造成代码难以维护
​​​​​​2:​栈撕裂造成异常无法向上跑出

采用协程解决,见后续博客。

 

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- gamedaodao.com 版权所有 湘ICP备2022005869号-6

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务