同步 异步 阻塞和非阻塞

基本事实:

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:​栈撕裂造成异常无法向上跑出

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值