并发编程(线程)——定时器,协程,greenlet模块,gevent模块,单线程的套接字并发,asyncio模块

一、定时器

Timer的父类是Thread,所以定时器这里用的是线程

# 多长时间之后执行一个任务
from threading import Timer


def task(name):
    print('我是大帅比--%s'%name)


if __name__ == '__main__':
    # t = Timer(2, task,args=('lqz',))  # 本质是开了个线程,延迟一秒执行  Timer(秒数,函数,arges=一个元组)
    t = Timer(2, task,kwargs={'name':'lqz'})  # 本质是开了个线程,延迟一秒执行
    t.start()

二、协程

协程是为了实现单线程下的并发,属性线程下
协程要解决的问题:保存状态+切换
yield:生成器,只要函数中有yield关键字,这个函数就是生成器,通过yield可以实现保存状态+切换
import time

# 串行执行
def func1():
    for i in range(100000000):
        i += 1


def func2():
    for i in range(100000000):
        i += 1


if __name__ == '__main__':
    ctime = time.time()
    func1()
    func2()
    print(time.time() - ctime)  # 7.03256796435


# 通过yield,实现保存状态加切换(自定义的切换,并不是遇到io才切,所有它并不能节约时间)
# 单纯的切换,不但不会提高效率,反而会讲低效率
def func1():
    for i in range(100000000):
        i += 1
        yield


def func2():
    g=func1() # 先执行一下func1
    for i in range(100000000):
        i += 1
        next(g) # 回到func1执行


if __name__ == '__main__':
    ctime = time.time()
    func2()
    print(time.time() - ctime) #14.764776706695557



# 协程并不是真是存在的某个东西,而是程序员臆想出来的
# 程序员控制,不让自己的程序遇到io,看上去,就实现并发了

'''
优点如下:
协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
单线程内就可以实现并发的效果,最大限度地利用cpu

缺点如下: 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
总结协程特点: 必须在只有一个单线程里实现并发 修改共享数据不需加锁 用户程序里自己保存多个控制流的上下文栈(需要保存状态) 附加:一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制))
'''

三、greenlet模块

协程相关模块

from greenlet import greenlet
import time
# 遇到io不会切,初级模块,gevent模块基于它写的,处理io切换
def eat():
    print('我吃了一口')
    time.sleep(1)
    # p.switch()
    print('我又吃了一口')
    # p.switch()


def play():
    print('我玩了一会')
    e.switch()
    print('我又玩了一会')


if __name__ == '__main__':
    e = greenlet(eat)
    p = greenlet(play)
    e.switch()

四、gevent模块

协程相关模块

# gevent基于greenlet写的,实现了遇见io自动切换
import gevent
import time

def eat(name):
    print('%s 吃了一口' % name)
    gevent.sleep(1)  # io操作
    print('%s 又吃了一口' % name)


def play(name):
    print('%s 玩了一会' % name)
    gevent.sleep(2)
    print('%s 又玩了一会' % name)


if __name__ == '__main__':
    ctim = time.time()
    e = gevent.spawn(eat,'lqz')
    p = gevent.spawn(play,'lqz')
    e.join() # 等待e执行完成
    p.join()
    print('')
    print(time.time() - ctim)  #2.0165154933929443

    这个程序执行完成,最少需要多长时间 2s多一点
    ctim=time.time()
    eat('lqz')
    play('lqz')
    print(time.time()-ctim) # 3.0190377235412598

gevent模块加上猴子补丁

from gevent import monkey;monkey.patch_all()#这句话必须写
import gevent
import time

def eat(name):
    print('%s 吃了一口' % name)
    time.sleep(1)  # io操作,被猴子补丁替换之后,gevent.sleep()
    print('%s 又吃了一口' % name)


def play(name):
    print('%s 玩了一会' % name)
    time.sleep(2)
    print('%s 又玩了一会' % name)


if __name__ == '__main__':
    ctim = time.time()
    e = gevent.spawn(eat,'lqz')
    p = gevent.spawn(play,'lqz')
    e.join() # 等待e执行完成
    p.join()
    print('')
    print(time.time() - ctim)  #2.0165154933929443

五、单线程的套接字并发

使用gevent实现单线程下的套接字并发效果

六、asyncio模块

3.4及其之前版本使用的asyncio模块

import time
import asyncio

# 把普通函数变成协程函数
# 3.4及其以前这么写,3.5开始就弃用了,3.5版本及其之后用了不报错但会提示已经启用
@asyncio.coroutine
def task():
    print('开始了')
    yield from asyncio.sleep(1)  #asyncio.sleep(1)模拟io
    print('结束了')


loop=asyncio.get_event_loop()  # 获取一个时间循环对象#

# 协程函数加括号,并不会真正的去执行,它需要提交给loop,让loop循环着去执行
# 协程函数列表

ctime=time.time()
t=[task(),task()]
loop.run_until_complete(asyncio.wait(t))
loop.close()
print(time.time()-ctime)

现在使用的asyncio模块用法

import time
import asyncio
from threading import current_thread
# 表示我是协程函数,等同于3.5之前的装饰器
async def task():
    print('开始了')
    print(current_thread().name)
    await asyncio.sleep(3)  # await等同于原来的yield from
    print('结束了')

async def task2():
    print('开始了')
    print(current_thread().name)
    await asyncio.sleep(2)
    print('结束了')

loop=asyncio.get_event_loop()

ctime=time.time()
t=[task(),task2()]
loop.run_until_complete(asyncio.wait(t))
loop.close()
print(time.time()-ctime)

 ---40---

原文地址:https://www.cnblogs.com/guojieying/p/13571829.html