跳转至

协程


代码1(yield)

import time


def work_one():
    for _ in range(3):
        print('-----协程1-----')
        yield
        time.sleep(0.5)


def work_two():
    for _ in range(3):
        print('-----协程2-----')
        yield
        time.sleep(0.5)


def main():
    wo, wt = work_one(), work_two()
    for _ in range(3):
        next(wo)
        next(wt)


if __name__ == '__main__':
    main()

代码2(greenlet)

import time

from greenlet import greenlet


def main():

    def work_one():
        while True:
            print('-----A-----')
            gl_two.switch()
            time.sleep(0.5)


    def work_two():
        while True:
            print('-----B-----')
            gl_one.switch()
            time.sleep(0.5)



    gl_one = greenlet(work_one)
    gl_two = greenlet(work_two)

    gl_one.switch()


if __name__ == '__main__':
    main()

通过greenlet实现的协程,需要人工切换

代码3(gevent)

用法1: 使用monkey.patch_all()实现,当遇到耗时情况时自动切换协程

import time

import gevent
from gevent import monkey

# 给程序打补丁, 有耗时操作(例如time.sleep)时
monkey.patch_all()


def work1():
    while True:
        print('work1...')
        time.sleep(1)


def work2():
    while True:
        print('work2...')
        time.sleep(1)


def main():
    w1 = gevent.spawn(work1)
    w2 = gevent.spawn(work2)
    w1.join()
    w2.join()


if __name__ == '__main__':
    main()

用法2: 不使用monkey.patch_all()的情况下要实现协程的自动切换,需使用gevent中自己的耗时模块

import gevent


def work1():
    while True:
        print('work1...')
        # 将程序中用到的耗时操作的代码,使用gevent中自己实现的模块
        gevent.sleep(1)


def work2():
    while True:
        print('work2...')
        # 使用gevent中自己实现的模块,不需要monkey.patch_all()
        gevent.sleep(1)


def main():
    w1 = gevent.spawn(work1)
    w2 = gevent.spawn(work2)
    w1.join()
    w2.join()


if __name__ == '__main__':
    main()

用法3: 批量join协程和传参

import gevent


def work1(msg):
    while True:
        print(msg)
        gevent.sleep(1)


def work2(msg):
    while True:
        print(msg)
        gevent.sleep(1)


def main():
    # joinall可批量添加协程
    gevent.joinall(
        [
            # "I'm work1" 即是参数,会传给前面的函数,即work1
            gevent.spawn(work1, "I'm work1"),
            gevent.spawn(work2, "I'm work2")
        ]
    )


if __name__ == '__main__':
    main()

通过gevent实现的协程,不需要人工切换,当检测到耗时操作时会自动切换

总结

  • 进程是资源分配的单位,同一个进程下的所有线程共享全部资源
  • 线程是操作系统调度的单位
  • 进程切换需要的资源很最大,效率很低
  • 在不考虑GIL的情况下,线程切换需要的资源一般,效率一般
  • 协程切换任务资源很小,效率高
  • 进程、线程不受控制,协程受控制
  • 协程是并发不是并行