多线程和多进程

异步
同步: 就是女儿叫你起床,你还没有起来,她一直在床边等着也不去早读,等你起来之后,再去早读。
异步:就是女儿叫你起床,你还没有起来,她对着你房间喊一声“起床了”,然后就不管了,自己去早读了。
异步应用
为完成某个任务,不同程序单元之间过程中无需通信协调,也能完成任务的方式。
不相关的程序单元之间可以是异步的。
例如,爬虫下载网页。调度程序调用下载程序后,即可调度其他任务,而无需与该下载任务保持通信以协调
行为。不同网页的下载、保存等操作都是无关的,也无需相互通知协调。这些异步操作的完成时刻并不确
定。异步意味着无序。
如果在某程序的运行时,能根据已经执行的指令准确判断它接下来要进行哪个具体操作,那它是同步程序,反之则
为异步程序。(无序与有序的区别)
同步/异步、阻塞/非阻塞并非水火不容,要看讨论的程序所处的封装级别。例如购物程序在处理多个用户的浏览请
求可以是异步的,而更新库存时必须是同步的
status =0
time.sleep(random.randint(1,10))
if name == ' main ':
d = threading.Thread(target=door)
p = threading.Thread(target=person)
d.start()
p.start()
# -*- coding: UTF-8 -*-
# 文件名:asyn_a.py
from multiprocessing import Pool
import time
import os
def test():
print("---进程池中的进程---pid=%d,ppid=%d--" % (os.getpid(), os.getppid()))
for i in range(3):
print("----%d---" % i)
time.sleep(1)
return "hahah"
def test2(args):
print("---callback func--pid=%d" % os.getpid())
print("---callback func--args=%s" % args)

我们看到,异步调用实际想要同时结束进程,但是线程池中的其他进程有需要执行任务,等待执行任务完成结束去
与其他进程回合,这里的参数通过回调,直至程序结束。
协程
协程(coroutine),又称为微线程,纤程。(协程是一种用户态的轻量级线程)
作用:在执行A 函数的时候,可以随时中断,去执行B 函数,然后中断继续执行A 函数(可以自动切换),注意这
一过程并不是函数调用(没有调用语句),过程很像多线程,然而协程只有一个线程在执行
if name == ' main ':
# 创建进程池及制定数量
pool = Pool(3)
# 非阻塞支持回调
pool.apply_async(func=test, callback=test2)
time.sleep(5)
print("----主进程-pid=%d----" % os.getpid())

对于单线程下,我们不可避免程序中出现io 操作,但如果我们能在自己的程序中(即用户程序级别,而非操作系统级
别)控制单线程下的多个任务能在一个任务遇到io 阻塞时就将寄存器上下文和栈保存到其他地方,切换到另外一个任
务去计算。在任务切回来的时候,恢复先前保存的寄存器上下文和栈,这样就保证了该线程能够最大限度地处 于就
绪态,即随时都可以被cpu 执行的状态,相当于我们在用户程序级别将自己的io 操作最大限度地隐藏起来,从而可以
迷惑操作系统,让其看到:该线程好像是一直在计算,io 比较少,从而更多的将cpu的执行权限分配给我们 的线程
(注意:线程是CPU 控制的,而协程是程序自身控制的)
协作的标准
必须在只有一个单线程里实现并发
□ 修改共享数据不需加锁
□ 用户程序里自己保存多个控制流的上下文栈
□ 一个协程遇到IO 操作自动切换到其它协程

由于自身带有上下文和栈,无需线程上下文切换的开销,属于程序级别的切换,操作系统完全感知不到,因而更加轻
量级;无需原子操作的锁定及同步的开销;方便切换控制流,简化编程模型,单线程内就可以实现并发的效果,最
大限度地利用cpu,且可扩展性高,成本低(注:一个CPU 支持上万的协程都不是问题。所以很适合用于高并发处
理)
他的缺点:无法利用多核资源:协程的本质是个单线程,它不能同时将单个CPU 的多个核用上,协程需要和进程配合
才能运行在多CPU 上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu 密集型应用。进行阻塞
(Blocking)操作(如IO 时)会阻塞掉整个程序
greenlet
Gevent 模块
Gevent 是一个第三方库,可以轻松通过gevent 实现并发同步或异步编程,在gevent中用到的主要模式是
Greenlet,它是以C 扩展模块形式接入Python 的轻量级协程。Greenlet 全部运行在主程序操作系统进程的内部,
但他们被协作式地调度。
当一个greenlet遇到IO操作时,比如访问网络/睡眠等待,就自动切换到其他的greenlet,等到IO操作完成,再在适
当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,
就保证总有greenlet在运行,而不是等待IO。同时也因为只有一个线程在执行,会极大的减少上下文切换的成本。
安装模块
代码:
# -*- coding: UTF-8 -*-
# 文件名:greenlet_a.py
from greenlet import greenlet
def attack(name):
print(f'{name} :我要买包!') # 2
gree_b.switch('吕布') # 3
print(f'{name} :我要去学编程!') # 6
gree_b.switch() # 7
def player(name):
print(f'{name} :买买买!! ') # 4
gree_a.switch() # 5
print(f'{name} :一定去马士兵教育!!!!') # 8
gree_a = greenlet(attack)
gree_b = greenlet(player)
gree_a.switch('貂蝉') # 可以在第一次switch 时传入参数,以后都不需要#1
pip install gevent
# -*- coding: UTF-8 -*-

注意:上例gevent.sleep(2)模拟的是gevent 可以识别的io 阻塞;
而time.sleep(2)或其他的阻塞,gevent 是不能直接识别的,需要加入一行代码monkey.patch_all(),这行代码需在
time,socket 模块之前。
async io 异步IO asyncio 是python3.4 之后的协程模块,是python 实现并发重要的包,这个包使用事件循环驱动
实现并发。事件循环是一种处理多并发量的有效方式,在维基百科中它被描述为「一种等待程序分配事件或消息的
编程架构」,我们可以定义事件循环来简化使用轮询方法来监控事件,通俗的说法就是「当A 发生时,执行B」。事
件循环利用poller 对象,使得程序员不用控制任务的添加、删除和事件的控制。事件循环使用回调方法来知道事件
的发生。
# 文件名:gevent_a.py
import gevent
def gf(name):
print(f'{name} :我要买包!') # 2
gevent.sleep(2) # 3
print(f'{name} :我要去学编程!') # 6
def bf(name):
print(f'{name} :买买买!! ') # 4
gevent.sleep(2)
print(f'{name} :一定去马士兵教育!!!!') # 8
geven_a = gevent.spawn(gf, ' 小 乔 ')
geven_b = gevent.spawn(bf, name='周瑜')
gevent.joinall([geven_a, geven_b])

看代码
# -*- coding: UTF-8 -*-
# 文件名:asyn_b.py
import asyncio
@asyncio.coroutine # python3.5 之 前
def func_a():

总结
串行、并行与并发的区别
for i in range(5):
print('协程——a!!')
yield from asyncio.sleep(1)
async def func_b(): # python3.5 之 后
for i in range(5):
print(' 协 程 ——b!!!')
await asyncio.sleep(2)
# 创建协程对象
asy_a = func_a()
asy_b= func_b()
# 获取事件循环
loop = asyncio.get_event_loop()
# 监听事件循环
loop.run_until_complete(asyncio.gather(asy_a, asy_b))
# 关闭事件
loop.close()
协程的嵌套:使用async可以定义协程,协程用于耗时的io操作,我们也可以封装更多的io操作过程,这样就实现了嵌套
的协程,即一个协程中await了另外一个协程,如此连接起来。
# -*- coding: UTF-8 -*-
# 文件名:asyn_c.py
import asyncio
async def compute(x, y):
print(f"compute: {x}+{y} ...")
await asyncio.sleep(1)
return x + y
async def print_sum(x, y):
result = await compute(x, y)
print(f"{x}+{y}={result}")
loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()

并行:指的是任务数小于等于 cpu核数,即任务真的是一起执行的
并发:指的是任务数多余 cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行(实际上
总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)
进程与线程的区别

1. 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
2. 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
3. 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资
源(如打开文件和信号),某进程内的线程在其它进程不可见;
4. 调度和切换:线程上下文切换比进程上下文切换要快得多。
有一个老板想要开个工厂进行生产某件商品(例如:手机)他需要花一些财力物力制作一条生产线,这个生产线上有很多
的器件以及材料这些所有的为了能够生产手机而准备的资源称之为:进程。只有生产线是不能够进行生产的,所以老板的
找个工人来进行生产,这个工人能够利用这些材料最终一步步的将手机做出来,这个来做事情的工人称之为:线程这个老
板为了提高生产率,想到 3种办法:
1. 在这条生产线上多招些工人,一起来做手机,这样效率是成倍増长,即单进程多线程方式
2. 老板发现这条生产线上的工人不是越多越好,因为一条生产线的资源以及材料毕竟有限,所以老板又花了些财力物力购
置了另外一条生产线,然后再招些工人这样效率又再一步提高了,即多进程多线程方式
3. 老板发现,现在已经有了很多条生产线,并且每条生产线上已经有很多工人了(即程序是多进程的,每个进程中又有多
个线程),为了再次提高效率,老板想了个损招,规定:如果某个员工在上班时临时没事或者再等待某些条件(比如等待
另一个工人生产完谋道工序之后他才能再次工作),那么这个员工就利用这个时间去做其它的事情,那么也就是说:如果
一个线程等待某些条件,可以充分利用这个时间去做其它事情,其实这就是:协程方式

进程:拥有自己独立的堆和栈,既不共享堆,也不共享栈,进程由操作系统调度;进程切换需要的资源很最大,效
率很低 线程:拥有自己独立的栈和共享的堆,共享堆,不共享栈,标准线程由操作系统调度;线程切换需要的资源一
般,效率一般(当然了在不考虑 GIL的情况下) 协程:拥有自己独立的栈和共享的堆,共享堆,不共享栈,协程由
程序员在协程的代码里显示调度;协程切换任务资源很小,效率高 多进程、多线程根据 cpu核数不一样可能是并行
的,但是协程是在一个线程中所以是并发 选择技术考虑的因素:切换的效率、数据共享的问题、数据安全、是否 需
要并发

  • 19
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值