python高级编程(五)--多线程-协程

来源:互联网 发布:西门子tia博途软件 编辑:程序博客网 时间:2024/05/23 00:45

1. 简单协程

  • 概念:协程,又称为微线程,纤程,英文名Coroutine。协程的作用,是在执行函数A时,可以随时中断,去执行函数B,然后中断继续执行函数A(可以自由切换)。但这一过程并不是函数调用(没有调用语句),这一整个过程看似像多线程,然而协程只有一个线程执行。

  • 简单实现协程:

import timedef work1():    while True:        print("------work1----")        yield        time.sleep(0.5)def work2():    while True:        print("------work2-----")        yield        time.sleep(0.5)def main():    w1 = work1()    w2 = work2()    while True:        next(w1)        next(w2)if __name__ == "__main__":    main()

运行结果:
—-work1—
—-work2—
—-work1—
—-work2—
—-work1—
—-work2—
…省略…

  • 协程的好处:

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

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

2. greenlet

为了更好使用协程来完成多任务,python中greenle模块对其封装,从而使得切换任务变得更加简单。

  • greenlet的使用:
# coding=utf-8from greenlet import greenletimport timedef test1():    whle True:        print("---A----")        gr2.switch()        time.sleep(0.5)def test2():    while True:        print("----B----")        gr1.switch()        time.sleep(0.5)gr1 = greenlet(test1)gr2 = greenlet(test2)# 切换到gr1中运行gr1.switch()

运行效果:
—A–
—B–
—A–
—B–
—A–
—B–
…省略…

3. gevent

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

  • 能够自动切换任务的模块gevent
  • 其原理是当一个greenlet遇到IO(指的是input output输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
  • 由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动化切换协程,就保证总有greenlet在运行,而不是等待IO

  • gevent使用

import geventdef f(n)    for i in range(n):        print(gevent.getcurrent(), i)g1 = gevent.spawn(f, 5)g2 = gevent.spawn(f, 5)g3 = gevent.spawn(f, 5)g1.join()g2.join()g3.join()

运行结果

<Greenlet at 0x10e49f550: f(5)> 0<Greenlet at 0x10e49f550: f(5)> 1<Greenlet at 0x10e49f550: f(5)> 2<Greenlet at 0x10e49f550: f(5)> 3<Greenlet at 0x10e49f550: f(5)> 4<Greenlet at 0x10e49f910: f(5)> 0<Greenlet at 0x10e49f910: f(5)> 1<Greenlet at 0x10e49f910: f(5)> 2<Greenlet at 0x10e49f910: f(5)> 3<Greenlet at 0x10e49f910: f(5)> 4<Greenlet at 0x10e49f4b0: f(5)> 0<Greenlet at 0x10e49f4b0: f(5)> 1<Greenlet at 0x10e49f4b0: f(5)> 2<Greenlet at 0x10e49f4b0: f(5)> 3<Greenlet at 0x10e49f4b0: f(5)> 4
  • gevent自动切换任务
import geventdef f(n):    for i in range(n):        print(gevent.getcurrent(), i)        # 用来模拟一个耗时操作,注意不是time模块中的sleep        gevent.sleep(1)g1 = gevent.spawn(f, 5)g2 = gevent.spawn(f, 5)g3 = gevent.spawn(f, 5)g1.join()g2.join()g3.join()

运行结果:

<Greenlet at 0x7fa70ffa1c30: f(5)> 0<Greenlet at 0x7fa70ffa1870: f(5)> 0<Greenlet at 0x7fa70ffa1eb0: f(5)> 0<Greenlet at 0x7fa70ffa1c30: f(5)> 1<Greenlet at 0x7fa70ffa1870: f(5)> 1<Greenlet at 0x7fa70ffa1eb0: f(5)> 1<Greenlet at 0x7fa70ffa1c30: f(5)> 2<Greenlet at 0x7fa70ffa1870: f(5)> 2<Greenlet at 0x7fa70ffa1eb0: f(5)> 2<Greenlet at 0x7fa70ffa1c30: f(5)> 3<Greenlet at 0x7fa70ffa1870: f(5)> 3<Greenlet at 0x7fa70ffa1eb0: f(5)> 3<Greenlet at 0x7fa70ffa1c30: f(5)> 4<Greenlet at 0x7fa70ffa1870: f(5)> 4<Greenlet at 0x7fa70ffa1eb0: f(5)> 4
from gevent import monkeyimport geventimport randomimport time# 有耗时操作时需要monkey.patch_all()  # 将程序中用到的耗时操作的代码,换为gevent中自己实现的模块def coroutine_work(coroutine_name):    for i in range(10):        print(coroutine_name, i)        time.sleep(random.random())gevent.joinall([        gevent.spawn(coroutine_work, "work1"),        gevent.spawn(coroutine_work, "work2")])

运行结果:

运行结果work1 0work2 0work1 1work1 2work1 3work2 1work1 4work2 2work1 5work2 3work1 6work1 7work1 8work2 4work2 5work1 9work2 6work2 7work2 8work2 9
  • 通过gevent实现单线程下的多socket并发

server side

import sysimport socketimport timeimport geventfrom gevent import socket,monkeymonkey.patch_all()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(8001)

client side

import socketHOST = 'localhost'    # The remote hostPORT = 8001           # The same port as used by the servers = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.connect((HOST, PORT))while True:    msg = bytes(input(">>:"),encoding="utf8")    s.sendall(msg)    data = s.recv(1024)    #print(data)    print('Received', repr(data))s.close()
原创粉丝点击