多进程、多线程和协程

简书:https://www.jianshu.com/p/1cce7b37b80a
CSDN:https://blog.csdn.net/u012052168/article/details/89156143

一、前言

计算机执行的任务可以分为IO密集型任务和计算任务,计算机的CPU计算是非常快的,而程序常常会因为等待IO而阻塞。比如对网站进行请求时,如果网站的返回速度很慢,而程序一直在等待网站相应,同理进行IO读写操作时,程序也一直在等待。所以,本文就借此聊一下Python中的进程、线程和协程。
关于Python中进程和线程,我们可以从以下2个角度来看,而协程则放在后面叙述。

计算机硬件角度:
计算机的核心是CPU,它承担着所有的计算任务;
一个CPU,在一个时间片内只能运行一个程序。

从操作系统的角度:
进程和线程,都是一种CPU的执行单元。
进程:表示一个程序的上下文执行活动(打开、执行、保存...)  
线程:进程执行程序时候的最小调度单位(执行a,执行b...)
一个程序至少有一个进程,一个进程至少有一个线程。

聊完计算机内的进程和线程,看下两个概念:并行和并发

并行:如果你的计算机有4个CPU核心,不同的程序就分配给不同的CPU来运行,也就是同时可以运行4个程序。
并发:单个CPU核心,在一个时间切片里一次只能运行一个程序,如果需要运行多个程序,则串行执行。举个栗子,
程序a运行1s,接下来换到程序b运行1s,因为计算机运行速度很快,看起来像是2个程序在并行,但实际是两个程
序在串行。

二、概念

1、阻塞
阻塞状态指程序未得到所需计算资源时被挂起的状态。程序在等待某个操作完成期间,自身无法继续干别的事情,则称该程序在该操作上是阻塞的。

常见的阻塞形式有:网络 I/O 阻塞、磁盘 I/O 阻塞、用户输入阻塞等。阻塞是无处不在的,包括 CPU 切换上下文时,所有的进程都无法真正干事情,它们也会被阻塞。如果是多核 CPU 则正在执行上下文切换操作的核不可被利用。
2、非阻塞
程序在等待某操作过程中,自身不被阻塞,可以继续运行干别的事情,则称该程序在该操作上是非阻塞的。

非阻塞并不是在任何程序级别、任何情况下都可以存在的。
仅当程序封装的级别可以囊括独立的子程序单元时,它才可能存在非阻塞状态。

非阻塞的存在是因为阻塞存在,正因为某个操作阻塞导致的耗时与效率低下,我们才要把它变成非阻塞的。
3、同步
不同程序单元为了完成某个任务,在执行过程中需靠某种通信方式以协调一致,称这些程序单元是同步执行的。
简言之,同步意味着有序。
4、异步
为完成某个任务,不同程序单元之间过程中无需通信协调,也能完成任务的方式,不相关的程序单元之间可以是异步的。
简言之,异步意味着无序。
5、进程
进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。进程是操作系统动态执行的基本单元。
6、线程
一个进程中包含若干线程,当然至少有一个线程,线程可以利用进程所拥有的资源。线程是独立运行和独立调度的基本单元。
7、协程
协程是一种用户态的轻量级线程。协程无需线程上下文切换的开销,也无需原子操作锁定及同步的开销。
8、多进程
多进程就是利用 CPU 的多核优势,在同一时间并行地执行多个任务。多进程模式优点就是稳定性高,因为一个子进程崩溃了,不会影响主进程和其他子进程,但是操作系统能同时运行的进程数是有限的。
9、多线程、
多线程模式通常比多进程快一点,但是也快不到哪去,而且,多线程模式致命的缺点就是任何一个线程挂掉都可能直接造成整个进程崩溃,因为所有线程共享进程的内存。

二、多进程

1.单一进程

import multiprocessing
import time
 
def func(msg):
    for i in xrange(3):
    print(msg)
    time.sleep(1)
 
if __name__ == "__main__":
    p = multiprocessing.Process(target=func, args=("hello", ))
    p.start()
    p.join()
    print "Sub-process done."

2.多进程

import multiprocessing
def func(n) :   
    #获取当前进程名字
    name = multiprocessing.current_process().name 
    print(name,'starting')
    print("worker ", n)
    return 
if __name__ == '__main__' : 
    numList = [] 
    for i in xrange(5) : 
        p = multiprocessing.Process(target=func, args=(i,)) 
        numList.append(p) 
        p.start() 
        p.join() 
        print("Process end.")

3.进程池

当我们进行并行操作时,可以利用multiprocessing中的Process动态成生多个进程,但是手动去限制进程数量的话会特别繁琐,特别是进程数量特别大的时候,这时候适合使用进程池Pool。
Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来它。

#!/usr/bin/env python
#coding=utf-8
from multiprocessing import Pool
from time import sleep
from time,random
 
def get_url(name):
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print('name:%s,time:%s' %(name,(end-start)))

def main():
  pool = Pool(processes=3)  # 设置进程最大数量
  for i in range(11,20):
    result = pool.apply_async(get_url, (i,))
  pool.close()
  pool.join()
  if result.successful():
    print('successful')
 
if __name__ == "__main__":
    main()
创建容量为3的进程池,然后将get_url(i)依次传递给它,
ps aux | grep xxx.py ,查看进程
pool.apply_async()用来向进程池提交目标请求
pool.join()是用来等待进程池中的worker进程执行完毕,防止主进程在worker进程结束前结束。
pool.join()必须使用在pool.close()或者pool.terminate()之后。
close()跟terminate()的区别在于close()会等待池中的worker进程执行结束再关闭pool,而terminate()则是直接关闭。
result.successful()表示整个调用执行的状态,如果还有worker没有执行完,则会抛出AssertionError异常。

结果如下:
image.png

四、协程–async/await(Python3.5以上)

event_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足条件发生的时候,就会调用对应的处理方法。
coroutine:协程对象类型,我们可以将协程对象注册到事件循环中,它会被事件循环调用。我们可以使用 async 关键字来定义一个方法,这个方法在调用时不会立即被执行,而是返回一个协程对象。
task:任务,它是对协程对象的进一步封装,包含了任务的各个状态,比如 running、finished 等。
future:代表将来执行或没有执行的任务的结果,实际上和task没有本质区别

1、普通协程

2、多任务协程

import asyncio
import requests
import time
 
start = time.time()
async get(url):
    returen requests.get(url)
async def request():
    url = 'http://127.0.0.1:5000'
    print('Waiting for', url)
    response = await get(url)
    print('Get response from', url, 'Result:', response.text)
 
tasks = [asyncio.ensure_future(request()) for _ in range(5)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
 
end = time.time()
print('Cost time:', end - start)

其实这段代码还不是真正的异步,因为我们仅仅只是将IO操作的代码封装到async修饰的方法中了,需要使用支持异步请求的方式,才能实现真正的异步。

3、aiohttp–支持异步请求

import asyncio
import aiohttp
import time
 
start = time.time()
 
async def get(url):
    session = aiohttp.ClientSession()
    response = await session.get(url)
    await asyncio.sleep(1)   必须加await实现协程,asyncio.sleep()是一个子协程
    # await time.sleep(1)  time.sleep()不能与await搭配使用
    result = await response.text()
    session.close()
    return result
 
async def request():
    url = 'http://127.0.0.1:5000'
    print('Waiting for', url)
    result = await get(url)
    print('Get response from', url, 'Result:', result)
 
tasks = [asyncio.ensure_future(request()) for _ in range(5)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
 
end = time.time()
print('Cost time:', end - start)       

五、多进程+协程

在最新的 PyCon 2018 上,来自 Facebook 的 John Reese 介绍了 asyncio 和 multiprocessing 各自的特点,并开发了一个新的库,叫做 aiomultiprocess。
https://www.youtube.com/watch?v=0kXaLh8Fz3k

import asyncio
import aiohttp
import time
from aiomultiprocess import Pool

start = time.time()

async def get(url):
   session = aiohttp.ClientSession()
   response = await session.get(url)
   result = await response.text()
   session.close()
   return result

async def request():
   url = 'http://127.0.0.1:5000'
   urls = [url for _ in range(100)]
   async with Pool() as pool:
       result = await pool.map(get, urls)
       return result

coroutine = request()
task = asyncio.ensure_future(coroutine)
loop = asyncio.get_event_loop()
loop.run_until_complete(task)

end = time.time()
print('Cost time:', end - start)

两个关键字:async(定义一个协程),await(用来挂起阻塞方法的执行)
参考文章:
https://www.cnblogs.com/hanybblog/p/6225797.html
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431927781401bb47ccf187b24c3b955157bb12c5882d000
https://github.com/dano/aioprocessing
https://www.itread01.com/content/1547429522.html
https://cuiqingcai.com/6160.html
https://cuiqingcai.com/3335.html
https://www.cnblogs.com/TM0831/p/10274058.html
https://www.cnblogs.com/huangguifeng/p/7632799.html
https://www.cnblogs.com/zhen1996/p/9692988.html
http://www.cnblogs.com/linhaifeng/articles/7428874.html
https://blog.csdn.net/SL_World/article/details/86633611
https://www.itread01.com/content/1547429522.html
https://segmentfault.com/a/1190000008814676

如果您觉得文章对您有帮助,可以扫下支付宝红包码,支持一下。
3BDCAFBAB4D9360B3AF7A6BC7E7A0CC2.jpg

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值