python并发_协程

本文深入探讨了进程、线程与协程的概念及其在操作系统中的角色。解释了协程作为用户态轻量级线程的特性,以及如何通过Python的greenlet和gevent模块实现任务间的高效切换,达到并发效果,尤其是在爬虫等需要规避IO等待的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在操作系统中进程是资源分配的最小单位, 线程是CPU调度的最小单位。

协程:是单线程下的并发,又称微线程,纤程。英文名Coroutine。一句话说明:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。也就是说程序员用代码来控制切换.

参考: http://www.cnblogs.com/Eva-J/articles/8324673.html

# 进程 启动多个进程 进程之间是由操作系统负责调用
# 线程 启动多个线程 真正被CPU执行的最小单位实际是线程
    # 开启一个线程 创建一个线程 寄存器 堆栈
    # 关闭一个线程

# 协程
    # 本质上是一个线程
    # 能够在多个任务之间切换来节省一些IO时间
    # 协程中任务之间的切换也消耗时间,但是开销要远远小于进程线程之间的切换
# 实现并发的手段

import time
def consumer():
    while True:
        x = yield
        time.sleep(1)
        print('处理数据 :',x)

def producer():
    c = consumer()
    next(c)
    for i in range(10):
        time.sleep(1)
        print('生产数据:',i)
        c.send(i)

# 这个生产者消费者模型 模拟了程序的来回切换, 但是不能规避IO时间
producer()

 

使用pip3 install greenlet 和 pip3 install gevent 安装好模块,继续:

# 真正的协程模块就是使用greenlet完成的切换
from greenlet import greenlet

def eat():
    print('eating start')
    g2.switch()   # 切换到g2
    print('eating end')
    g2.switch()

def play():
    print('playing start')
    g1.switch()  # 切换到g1
    print('playing end')

g1 = greenlet(eat)  # 委托给g1
g2 = greenlet(play)
g1.switch()
  • greenlet可以实现协程,不过每一次都要人为的去指向下一个该执行的协程,显得太过麻烦。python还有一个比greenlet更强大的并且能够自动切换任务的模块gevent

参考:https://www.cnblogs.com/PrettyTom/p/6628569.html

# 协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。
import time
import gevent

def eat():
    print('eating start')
    # time.sleep(1)      # gevent 不能感知到time.sleep时间
    gevent.sleep(1)
    print('eating end')

def play():
    print('playing start')
    gevent.sleep(1)
    print('playing end')

g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
g1.join()
g2.join()

 

gevent的正确方式:

## 导入这句,将所有模块中的阻塞IO都打成一个包。就可以感知 time.sleep
from gevent import monkey;monkey.patch_all()
import time
import gevent
import threading

def eat():
    print(threading.current_thread().getName())  # Dummy 假的,虚拟的。
    print(threading.current_thread())
    print('eating start')
    time.sleep(1.2)
    print('eating end')

def play():
    print(threading.current_thread().getName())
    print(threading.current_thread())
    print('playing start')
    time.sleep(1)
    print('playing end')

g1 = gevent.spawn(eat)   # 注册到协程,遇到IO将自动切换
g2 = gevent.spawn(play)
# g1.join()
# g2.join()
gevent.joinall([g1,g2])
print('master')
# 进程和线程的任务切换由操作系统完成
# 协程任务之间的切换由程序(代码)完成,只有遇到协程模块能识别的IO操作的时候,程序才会进行任务切换,实现并发的效果

 

同步和异步:

# 同步 和 异步
from gevent import monkey;monkey.patch_all()
import time
import gevent

def task(n):
    time.sleep(1)
    print(n)

def sync():
    for i in range(5):
        task(i)

def async():
    g_lst = []
    for i in range(5):
        g = gevent.spawn(task,i)
        g_lst.append(g)
    gevent.joinall(g_lst)  # for g in g_lst:g.join()

sync()   # 同步
async()  # 异步

 

爬虫时使用协程并发

# 协程 : 能够在一个线程中实现并发效果的概念
    #    能够规避一些任务中的IO操作
    #    在任务的执行过程中,检测到IO就切换到其他任务

# 多线程 被弱化了
# 协程 在一个线程上 提高CPU 的利用率
# 协程相比于多线程的优势 切换的效率更快

# 爬虫的例子
# 请求过程中的IO等待
from gevent import monkey;monkey.patch_all()
import gevent
from urllib.request import urlopen    # 内置的模块

def get_url(url):
    response = urlopen(url)
    content = response.read().decode('utf-8')
    return len(content)

g1 = gevent.spawn(get_url,'http://www.baidu.com')
g2 = gevent.spawn(get_url,'http://www.sogou.com')
g3 = gevent.spawn(get_url,'http://www.taobao.com')
g4 = gevent.spawn(get_url,'http://www.hao123.com')
g5 = gevent.spawn(get_url,'http://www.cnblogs.com')
gevent.joinall([g1,g2,g3,g4,g5])
print(g1.value)
print(g2.value)
print(g3.value)
print(g4.value)
print(g5.value)

ret = get_url('http://www.baidu.com')
print(ret)

 

转载于:https://www.cnblogs.com/frx9527/p/gevent.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值