Python编程:协程coroutine

简介: Python编程:协程coroutine

关于协程

协程 coroutine 微线程,一种用户态的轻量级线程


好处:

无需线程上下文切换的开销

无需原子操作锁定及同步的开销

方便切换控制流,简化编程模型

高并发+高扩展+低成本,一个cup支持上万的协程都不是问题


缺点:

无法利用多核资源,协程的本质是单线程,

进程阻塞blocking操作如io时会阻塞整个程序


单线程下实现并发效果:遇到io就切换


服务器处理模型:

1.一个进程处理一个请求

2.一个线程处理一个请求

3.主进程处理事件队列的请求


事件驱动模型:

多个事件 -> 消息队列 -> 处理线程


事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。

它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。

另外两种常见的编程范式是(单线程)同步以及多线程编程。


异步asynchronous


用户空间application 和 内核空间kernel

进程控制块(Processing Control Block)


参考:

http://www.cnblogs.com/alex3714/articles/5876749.html


io操作两个阶段:内核缓冲区数据准备,拷贝到应用程序地址空间


阻塞io blocking 一直等待

非阻塞io nonblocking 准备数据立刻返回

多路复用 select,poll,epoll

异步io 没有被阻塞


twisted框架


手动切换协程

from greenlet import greenlet
def foo1():
    print("foo11")
    gr2.switch()
    print("foo12")
    gr2.switch()
def foo2():
    print("foo21")
    gr1.switch()
    print("foo22")
gr1 = greenlet(foo1)  # 创建协程
gr2 = greenlet(foo2)
print("main")
gr1.switch()  # 切换协程
print("done")
"""
main
foo11
foo21
foo12
foo22
done
"""

自动切换协程

import gevent  # 第三方库
def foo1():
    print("foo11")
    gevent.sleep(2)
    print("foo12")
def foo2():
    print("foo21")
    gevent.sleep(2)
    print("foo22")
def foo3():
    print("foo31")
    gevent.sleep(0)
    print("foo32")
gevent.joinall([
    gevent.spawn(foo1),
    gevent.spawn(foo2),
    gevent.spawn(foo3)
])
"""
foo11
foo21
foo31
foo32
foo12
foo22
"""

gevent 多并发socket

import socket
import gevent
from gevent import monkey
monkey.patch_all()  # 猴子补丁
def server(port):
    s = socket.socket()
    s.bind(("0.0.0.0", port))
    s.listen(500)
    print("服务已启动")
    while True:
        conn, addr = s.accept()
        print(addr)
        gevent.spawn(handle_request, conn)
def handle_request(conn): # 处理请求的协程
    try:
        while True:
            data = conn.recv(1024)
            print(conn, data.decode())
            conn.send(data)
            if not data:
                conn.shutdown(socket.SHUT_WR)
    except Exception as e:
        print(e)
    finally:
        print("关闭", conn)
        conn.close()
if __name__ == "__main__":
    server(6969)

select 多并发socket

# fd  文件描述符
import select
import socket
import queue
server = socket.socket()
server.setblocking(False)  # 不阻塞
server.bind(("localhost", 6969))
server.listen(5)
print("服务已开启")
inputs = [server, ]  # server本身也是一个文件描述符
outputs = []
message_dict = {}
while True:
    # 如果没有任何 fd 就绪,就会阻塞在这里
    readable, writeable, exceptional = select.select(inputs, outputs, inputs)
    print("readable", readable)
    print("writeable", writeable)
    print("exceptional", exceptional)
    for r in readable:
        if r is server:  # server就绪,新连接来了
            conn, addr = r.accept()
            print("已连接", conn)
            conn.setblocking(False)
            inputs.append(conn)  # 为了不阻塞程序,收到连接对象后放入列表,如果接收到信息,fd就绪
            message_dict[conn] = queue.Queue()  # 新建消息队列,不立刻返回,逐个处理
        else:  # 如果不是服务器,就是客户端
            data = r.recv(1024)
            if not data:
                print("连接断开")
            else:
                print("收到数据", data.decode("utf-8"))
                message_dict[r].put(data)  # 放入消息队列
                outputs.append(r)  # 不影响其他客户端连接,后续处理发送信息
    for s in outputs:  # 要返回给客户端的连接表
        data = message_dict[s].get()
        s.send(data)
        outputs.remove(s)  # 处理完就删除
    for e in exceptional:  # 删除异常连接
        if e in outputs:
            outputs.remove(e)
        inputs.remove(e)
        del message_dict[e]

selectors 多并发socket

import selectors
import socket
def accept(server, mask):  # 接受连接
    conn, addr = server.accept()
    print("conn:", conn, "addr:", addr, "mask:", mask)
    conn.setblocking(False)
    selector.register(conn, selectors.EVENT_READ, action)
def action(conn, mask):  # 接收数据
    data = conn.recv(1024)
    if data:
        print("conn:", conn, "data:", data.decode("utf-8"))
        conn.send(data)
    else:
        print("断开连接")
        conn.close()
        selector.unregister(conn)
server = socket.socket()
server.setblocking(False)
address = ("localhost", 6969)
server.bind(address)
server.listen(1000)
selector = selectors.DefaultSelector()
selector.register(server, selectors.EVENT_READ, accept)  # accept回调函数
print("服务启动")
while True:
    events = selector.select()  # 默认阻塞,有活动则返回活动列表
    for key, mask in events:
        callback = key.data  # accept
        callback(key.fileobj, mask)  # fileobj 文件句柄

异步爬虫(并行)

from urllib import request
from gevent import monkey
import time, gevent
monkey.patch_all()  # 把当前程序所有io操作都做上标记
def get_html(url):
    response = request.urlopen(url)
    html = response.read()
    print("receive:", len(html))
urls = [
    "https://www.python.org/",
    "https://www.yahoo.com/",
    "https://github.com/"
    ]
start_time = time.time()
for url in urls:
    html = get_html(url)
end_time = time.time()
print("串行:", end_time - start_time)
async_start_time = time.time()
gevent.joinall([
    gevent.spawn(get_html, urls[0]),
    gevent.spawn(get_html, urls[1]),
    gevent.spawn(get_html, urls[2])
])
async_end_time = time.time()
print("并行:", async_end_time - async_start_time)
"""
receive: 48893
receive: 511654
receive: 52225
串行: 4.339118003845215
receive: 48893
receive: 502566
receive: 52223
并行: 1.4489901065826416
"""

相关文章
|
1月前
|
人工智能 数据可视化 数据挖掘
探索Python编程:从基础到高级
在这篇文章中,我们将一起深入探索Python编程的世界。无论你是初学者还是有经验的程序员,都可以从中获得新的知识和技能。我们将从Python的基础语法开始,然后逐步过渡到更复杂的主题,如面向对象编程、异常处理和模块使用。最后,我们将通过一些实际的代码示例,来展示如何应用这些知识解决实际问题。让我们一起开启Python编程的旅程吧!
|
1月前
|
存储 数据采集 人工智能
Python编程入门:从零基础到实战应用
本文是一篇面向初学者的Python编程教程,旨在帮助读者从零开始学习Python编程语言。文章首先介绍了Python的基本概念和特点,然后通过一个简单的例子展示了如何编写Python代码。接下来,文章详细介绍了Python的数据类型、变量、运算符、控制结构、函数等基本语法知识。最后,文章通过一个实战项目——制作一个简单的计算器程序,帮助读者巩固所学知识并提高编程技能。
|
1月前
|
Unix Linux 程序员
[oeasy]python053_学编程为什么从hello_world_开始
视频介绍了“Hello World”程序的由来及其在编程中的重要性。从贝尔实验室诞生的Unix系统和C语言说起,讲述了“Hello World”作为经典示例的起源和流传过程。文章还探讨了C语言对其他编程语言的影响,以及它在系统编程中的地位。最后总结了“Hello World”、print、小括号和双引号等编程概念的来源。
114 80
|
2月前
|
存储 索引 Python
Python编程数据结构的深入理解
深入理解 Python 中的数据结构是提高编程能力的重要途径。通过合理选择和使用数据结构,可以提高程序的效率和质量
161 59
|
22天前
|
Python
[oeasy]python055_python编程_容易出现的问题_函数名的重新赋值_print_int
本文介绍了Python编程中容易出现的问题,特别是函数名、类名和模块名的重新赋值。通过具体示例展示了将内建函数(如`print`、`int`、`max`)或模块名(如`os`)重新赋值为其他类型后,会导致原有功能失效。例如,将`print`赋值为整数后,无法再用其输出内容;将`int`赋值为整数后,无法再进行类型转换。重新赋值后,这些名称失去了原有的功能,可能导致程序错误。总结指出,已有的函数名、类名和模块名不适合覆盖赋新值,否则会失去原有功能。如果需要使用类似的变量名,建议采用其他命名方式以避免冲突。
39 14
|
1月前
|
分布式计算 大数据 数据处理
技术评测:MaxCompute MaxFrame——阿里云自研分布式计算框架的Python编程接口
随着大数据和人工智能技术的发展,数据处理的需求日益增长。阿里云推出的MaxCompute MaxFrame(简称“MaxFrame”)是一个专为Python开发者设计的分布式计算框架,它不仅支持Python编程接口,还能直接利用MaxCompute的云原生大数据计算资源和服务。本文将通过一系列最佳实践测评,探讨MaxFrame在分布式Pandas处理以及大语言模型数据处理场景中的表现,并分析其在实际工作中的应用潜力。
82 2
|
1月前
|
小程序 开发者 Python
探索Python编程:从基础到实战
本文将引导你走进Python编程的世界,从基础语法开始,逐步深入到实战项目。我们将一起探讨如何在编程中发挥创意,解决问题,并分享一些实用的技巧和心得。无论你是编程新手还是有一定经验的开发者,这篇文章都将为你提供有价值的参考。让我们一起开启Python编程的探索之旅吧!
56 10
|
1月前
|
机器学习/深度学习 人工智能 Java
Python 语言:强大、灵活与高效的编程之选
本文全面介绍了 Python 编程语言,涵盖其历史、特点、应用领域及核心概念。从 1989 年由 Guido van Rossum 创立至今,Python 凭借简洁的语法和强大的功能,成为数据科学、AI、Web 开发等领域的首选语言。文章还详细探讨了 Python 的语法基础、数据结构、面向对象编程等内容,旨在帮助读者深入了解并有效利用 Python 进行编程。
|
1月前
|
机器学习/深度学习 人工智能 数据挖掘
探索Python编程的奥秘
在数字世界的海洋中,Python如同一艘灵活的帆船,引领着无数探险者穿梭于数据的波涛之中。本文将带你领略Python编程的魅力,从基础语法到实际应用,一步步揭开Python的神秘面纱。
48 12
|
1月前
|
IDE 程序员 开发工具
Python编程入门:打造你的第一个程序
迈出编程的第一步,就像在未知的海洋中航行。本文是你启航的指南针,带你了解Python这门语言的魅力所在,并手把手教你构建第一个属于自己的程序。从安装环境到编写代码,我们将一步步走过这段旅程。准备好了吗?让我们开始吧!