Python协程

一:介绍

# 协程主要解决的是IO操作,并行计算效果也不是很好
协程:又名纤程、微线程。协程是用户态的轻量级线程,完全由用户控制调度。另外,它只支持单线程下的并发
使用方式: 从python单线程内开启协程,但与python原生线程不同的是:一旦遇到IO,就会从应用程序层面(而非操作系统)控制切换,以此提高效率(非IO操作切换与效率无关)

操作系统控制线程的切换和协程的切换:
优点:
  # 非抢占式运行,不像线程和进程那样争夺运行权
  # 协程的切换开销比操作系统控制的线程小,操作系统还检测不到,因为属于程序级别的切换
  # 修改共享数据不需要加锁,判断状态即可
  # 没有锁

缺点:
  # 协程无法利用多核,因为本质是单线程,需要每个进程内开启多线程,每个线程开启协程。但可以利用 多进程+协程(解决并发的一种好方案) 利用多核
  # 协程出现阻塞,将会阻塞整个线程

 

import time


def parasite(name):
    print('吃包子了')
    while True:
        bum = yield
        print('[%s]吃了编号[-%s-]的包子' % (name, bum))

def chef():
    n = 0
    p1 = parasite('TOM')
    p2 = parasite('jack')
    next(p1)
    next(p2)
    while True:
        time.sleep(1)
        print('%s编号的包子做好了' % n)
        p1.send(n)
        p2.send(n)
        n += 1

chef()
yield 实现简单协程

第二:Greenlet 实现

greenlet(run=None, parent=None): 创建一个greenlet实例.
gr.parent:每一个协程都有一个父协程,当前协程结束后会回到父协程中执行,该 属性默认是创建该协程的协程.
gr.run: 该属性是协程实际运行的代码. run方法结束了,那么该协程也就结束了
gr.switch(*args, **kwargs): 切换另一层协程
gr.throw(): 切换到另一层协程,接着抛出一个异常

 

Greenlet 是C语言实现的,python自带的yield不能随意切换,但Greenlet是可以的
缺点:遇到IO会被阻塞
from
greenlet import greenlet def a(): print(1) b.switch() # 2.切换到b print(3) b.switch() # 4.切换到b def b(): print(2) a.switch() # 3.切换到 a print(4) a = greenlet(a) b = greenlet(b) a.switch() # 1.切换到A函数执行

第三:Gevent的实现方式

import gevent,time
from gevent.queue import Queue


def a():
    for i in range(10):

        print("a函数在生成编号[-%s-]的包子" % i)
        q.put(i)
        gevent.sleep(1)  # 阻塞后,立即切换

def b():
    for i in range(10):

        res = q.get()
        print("b函数在吃编号[-%s-]的包子" % res)
        gevent.sleep(1)  # 阻塞后,立即切换

q = Queue()
gevent.joinall(
    [
        gevent.spawn(a),
        gevent.spawn(b),
    ]
)

# 由此得知,gevent 比greenlet更好,因为遇到阻塞会自动切换
过程图:

 识别其他类型的阻塞:

from gevent import monkey;monkey.patch_all()  # 这个补丁要打在最前面,或者#coding=utf-8 的后面
import gevent,time
from gevent.queue import Queue


def a():
    for i in range(10):

        print("a函数在生成编号[-%s-]的包子" % i)
        q.put(i)
        time.sleep(1)  # 阻塞后,立即切换

def b():
    for i in range(10):

        res = q.get()
        print("b函数在吃编号[-%s-]的包子" % res)
        time.sleep(1)  # 阻塞后,立即切换

q = Queue()
gevent.joinall(
    [
        gevent.spawn(a),
        gevent.spawn(b),
    ]
)
from gevent import monkey;monkey.patch_all()
import gevent
from socket import *


def server():
    s = socket(AF_INET, SOCK_STREAM)
    s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    s.bind(('127.0.0.1', 888))
    s.listen(5)
    while True:
        conn, addr = s.accept()
        gevent.spawn(task, conn, addr)


def task(conn, addr):
    while True:
        data = conn.recv(1024)
        print('收到消息:', data)
        q = '收到你发来的消息'.encode('utf-8')
        conn.send(q)


if __name__ == '__main__':
    server()
Server端
from socket import *

c = socket(AF_INET, SOCK_STREAM)
c.connect(('127.0.0.1', 888))

while True:
    data = input('输入发送的消息:').encode('utf-8')
    c.send(data)
    msg = c.recv(1024)
    print(msg.decode('utf8'))
Client端

 

posted @ 2018-07-13 15:28  lei-jia-ming  阅读(160)  评论(0)    收藏  举报