码迷,mamicode.com
首页 > 其他好文 > 详细

并发编程之协程

时间:2019-09-29 09:27:53      阅读:115      评论:0      收藏:0      [点我收藏+]

标签:生成器   封装   pat   span   线程   多核   pen   接口   技术   

一、协程概述

1、什么是协程?

协程也被称为“微线程”,在一个线程中规定某个代码块的执行顺序线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。

2、为什么会有协程?

  对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。这样更加节约切换的时间。

3、协程的特点?

  • 不需要多线程的锁机制

因为只有一个线程,不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

  • 可以利用多核特点

通过多进程 + 协程既可以利用多核又可以发挥协程的高效率。

  • 从技术的角度来说,“协程就是你可以暂停执行的函数”

类似于python中的生成器

4、协程的应用场景

当程序中存在大量不需要CPU的操作时(IO),适用于协程。

二、greenlet、gevent模块

(一)greenlet

上面说了从技术层面上说“协程就是你可以暂停执行的函数”,相当于一个生成器,greenlet是一个用C实现的协程模块:

from greenlet import greenlet

def test1():
    print(9)    
    gr2.switch() #手动切换执行test2
    print(22)
    gr2.switch() #手动切换执行test2


def test2():
    print(2)
    gr1.switch() #手动切换执行test1
    print(36)


gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()  #开始执行test1
"""
输出:
9
2
22
36
"""

可以看出来greenlet需要手动控制来执行代码块的。

(二)gevent

这个模块比上一个模块智能许多,它不需要手动控制代码块的执行顺序,只是如果协程碰到I/O操作,就会自动切换。

import gevent

def test1():
    print(9)  #第2步
    gevent.sleep(1) #第3步
    print(22) #第7步

def test2():
    print(2)   #第4步
    gevent.sleep(0.3) #第5步
    print(36)  #第6步

gevent.joinall([
    gevent.spawn(test1), #第1步
    gevent.spawn(test2),
])
"""
输出:
9
2
36
22
"""

 当然,还可以使用gevent模块进行爬虫:

技术图片
import gevent
import requests

from gevent import monkey
monkey.patch_all()

def task(method,url,req_kwargs):
    response=requests.request(method=method,url=url,**req_kwargs)
    print(response.url,response.content)

#发送请求
gevent.joinall(
    {
        gevent.spawn(task,method=get,url=http://baidu.com/,req_kwargs={}),
        gevent.spawn(task, method=get, url=https://www.tudou.com/, req_kwargs={})

    }
)
gevent爬网页内容
技术图片
import gevent
import requests
from gevent.pool import Pool

from gevent import monkey
monkey.patch_all()

def task(method,url,req_kwargs):
    response=requests.request(method=method,url=url,**req_kwargs)
    print(response.url,response.content)

#发送请求
pool=Pool(5)
gevent.joinall(
    {
        pool.spawn(task,method=get,url=http://baidu.com/,req_kwargs={}),
        pool.spawn(task, method=get, url=https://www.tudou.com/, req_kwargs={})

    }
)
协程池发送请求

上面使用的是利用gevent进行并发发送请求,requests爬取网页内容,而grequests模块将两者进行结合,既有并发的特性又有requests的特性。

技术图片
#利用grequests进行并发 将requests和gevent进行封装

import grequests

request_list=[
    grequests.get(http://baidu.com/),
    grequests.get(https://www.tudou.com/)
]
response_list=grequests.map(request_list,size=5)
print(response_list) #[<Response [200]>, <Response [200]>]
grequests模块

 

并发编程之协程

标签:生成器   封装   pat   span   线程   多核   pen   接口   技术   

原文地址:https://www.cnblogs.com/shenjianping/p/11604817.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!