Python学习笔记——协程

前言:

    搜狗输入法输入协程,出来的是“携程”,我也是醉了。

正文:

  1. 进程是资源分配的单位
  2. 线程是CPU调度的单位
  3. 协程被称为微线程,是比线程更小的执行单元
    (先记住,具体怎么讲,我也不知道)

假设现在有三个线程,轮流占用CPU,具体怎么轮转是由CPU的调度算法决定的,不是开发者决定的,因为轮转的特别快,不可能让A从头执行到尾,然后再换B执行。。。为了执行的顺利,任何一个线程在占用CPU之前都要还原上次占用结束时的数据(简单来说如果是累加,1+…+100,加到50的时候,被切出去了,这是要把结果保存起来,下次轮转到了把结果取出来,继续累加)。

以上,线程在轮转的时候,需要保存数据,占用资源,而协程,相比之下占用资源远小于线程。


生成器版

import time

# 生成器
def A():
    while True:
        print('----A----')
        yield
        time.sleep(0.5)


def B(c):
    while True:
        print('----B----')
        # python 2.7 是 c.next()
        # python 3.x 是 next(c)
        next(c)
        time.sleep(0.5)


if __name__ == '__main__':
    a = A()
    B(a)

这里A是个生成器,结果是:
这里写图片描述

我们可以这样理解,函数A,B 两个函数在轮流占用CPU,函数的切换要比线程,进程的切换要保存的东西少了很多。所以占用资源要少了很多。

利用生成器是不是感觉比线程要麻烦一些呢?

别着急,看下面的内容。

greenlet版本

想简单就得做点准备工作
命令行
这里写图片描述

下载greenlet库。

from greenlet import *
import time


def task1():
    while True:
        print('------task1------')
        grt2.switch()
        time.sleep(0.5)


def task2():
    while True:
        print('------task2------')
        grt1.switch()
        time.sleep(0.5)


if __name__ == '__main__':
    grt1 = greenlet(task1)
    grt2 = greenlet(task2)
    grt1.switch()

结果为:
这里写图片描述

从这个例子中我们可以看出,具体切换函数的时候是我们开发者在控制着的,而不像在使用线程,进程的时候具体切换任务是CPU的调度算法决定的。

当然有时候我们不需要关心切换问题,完全可以交给程序去运行。这时候就有个更强大的库了。叫做gevent。

gevent版本

下载库
这里写图片描述

代码:

import gevent


def f(n):
    for i in range(n):
        print(gevent.getcurrent(), i)


def main():
    g1 = gevent.spawn(f, 5)
    g2 = gevent.spawn(f, 5)
    g3 = gevent.spawn(f, 5)
    g1.join()
    g2.join()
    g3.join()


if __name__ == '__main__':
    main()

结果:
这里写图片描述

g1,g2,g3,是三个协程,我们看着好像是顺序执行一样。

别着急继续往下看

import gevent


def f(n):
    for i in range(n):
        print(gevent.getcurrent(), i)
        #这里添加了一句话,注意不是time.sleep
        gevent.sleep(0.1)


def main():
    g1 = gevent.spawn(f, 5)
    g2 = gevent.spawn(f, 5)
    g3 = gevent.spawn(f, 5)
    g1.join()
    g2.join()
    g3.join()


if __name__ == '__main__':
    main()

生成器和greenlet是开发者手动切换协程,gevent如果一个协程遇到一个耗时操作的时候,就会自动切换到另一个协程。
协程经常使用在IO密集型程序中。IO就是频繁耗时操作。gevent强大就强大在这里。

下面我们看一个利用gevent完成的TCP服务器:

#这里我试过了不能用 from gevent import *
from gevent import socket, monkey, spawn, getcurrent
#在执行这句话的时候,编译器自己把下面的代码就修改成了自己可以识别的耗时操作。
#以至于到了耗时就可以自动切换协程。
monkey.patch_all()

conns = {}
addrs = {}

def handle_request(conn):
    print(getcurrent())
    while True:
        data = conn.recv(1024)
        if data:
            print("%s发来数据:%s" % (str(addrs[conn.fileno]), data))
            conn.send(data)
        else:
            print("%s断开连接" % str(addrs[conn.fileno]))
            del conns[conn.fileno]
            del addrs[conn.fileno]
            conn.close()
            break

def server(port):
    s = socket.socket()
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(('', port))
    s.listen(5)
    while True:
        print('等待新连接')
        cli, addr = s.accept()
        conns[cli.fileno] = cli
        addrs[cli.fileno] = addr
        spawn(handle_request, cli)

if __name__ == '__main__':
    server(7788)

结果:
这里写图片描述

协程,到此结束

后面一节开始学习正则表达式。

例子下载:
下载地址

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值