Python_协程

协程

协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:线程是一种用户态的轻量级线程。

协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置

优点:

  • 无需线程上下文切换的开销
  • 无需原子操作锁定及同步的开销
  • 方便切换控制流,简化编程模型
  • 高并发+高拓展+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理

缺点:

  • 无法利用多核资源:协程的本质是个单线程,它不能同时将单个CPU的多个核用上,协程需要和进程配合才能运行在多CPU上,当然我们日常所编写的绝大部分引用都没有这个必要,除非是CPU密集型应用。
  • 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

使用yield实现协程操作

import time
import queue

def consumer(name): #consumer是一个生成器只有在调用next()函数时才执行,遇到yield语句返回
    print("--->starting eating baozi...")
    while True:
        new_baozi = yield #yield不但可以返回一个值,它还可以接收调用者发出的参数
        print("[%s] is eating baozi %s" % (name, new_baozi))
        time.sleep(1)

def producer():
    con.__next__() #因为consumer已经是一个生成器,首先要生成一个生成器对象,然后用next()函数不断获取下一个返回值
    con2.__next__()
    n = 0
    while n < 5:
        n += 1
        print("\033[32;1m[producer]\033[0m is making baozi %s" % n)
        con.send(n) #唤醒生成器的同时,还给它传一个值,这个值就是yield接收的值
        con2.send(n)


if __name__ == "__main__":
    con = consumer("c1")
    con2 = consumer("c2")
    p = producer()

使用greenlet实现协程

from greenlet import greenlet

def test1():
    print(12)
    gr2.switch() #2、切换到test2
    print(34)
    gr2.switch()  #4、切换到test2

def test2():
    print(56)
    gr1.switch() #3、又切换到test1上次听下来的地方继续执行,print(34)
    print(78)

gr1 = greenlet(test1) #启动一个协程
gr2 = greenlet(test2)

gr1.switch() #1、运行test1

#-------------------------------------------------
12
56
34
78

使用gevent实现协程

gevent是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是greenlet,它是以C拓展模块形式接入python的轻量级协程。greenlet全部运行在主程序操作系统的内部,但它们被协作式地调度

import gevent

def foo():
    print("Running in foo")
    gevent.sleep(2) #触发切换
    print("Explicit context switch to foo again") #再次精确的切换到上下文foo

def bar():
    print("Explicit context to bar") #精确的切换到上下文bar
    gevent.sleep(1) #触发切换,返回到上次切换的位置(gevent.sleep(2)),直至其中一方的时间先结束,再进行下一步,更改时间试试
    print("Implicit context switch back to bar") #

gevent.joinall([ #启动两个协程,遇到IO阻塞时会自动切换任务
    gevent.spawn(foo), #生成
    gevent.spawn(bar),
])

通过gevent实现单线程下的多socket并发

server服务端

import sys
import socket
import time
import gevent

from gevent import socket, monkey
monkey.patch_all() #识别IO

def server(port):
    s = socket.socket()
    s.bind(("0.0.0.0", port))
    s.listen(500)
    while True:
        cli, addr = s.accept()
        gevent.spawn(handle_request,cli)

def handle_request(conn):
    try:
        while True:
            data = conn.recv(1024)
            print("recv:", data)
            conn.send(data)
            if not data:
                conn.shutdown(socket.SHUT_WR)

    except Exception as ex:
        print(ex)
    finally:
        conn.close()

if __name__ == "__main__":
    server(8888)

client客户端

import socket

HOST = "localhost"
PORT = 8888
s = socket.socket()
s.connect((HOST, PORT))
while True:
    msg = bytes(input(">>:"), encoding="utf8")
    s.sendall(msg)
    data = s.recv(1024)

    print("Received", repr(data))
s.close()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值