目录
1.进程
进程是一个实体。每个进程都有自己的地址空间(CPU分配)。实体空间包括三部分:
* 文本区域:存储处理器执行的代码。
* 数据区域:存储变量或进程执行期间使用的动态分配的内存。
* 堆栈:进程执行时调用的指令和本地变量。
进程是一个“执行中的程序”
程序是指令与数据的有序集合,程序本身是没有生命的,只有CPU赋予程序生命时(CPU执行程序),它才能成为一个活动的实体,称为“进程”
概括来说,进程就是一个具有独立功能的程序在某个数据集上的一次运行活动
进程的特点
* 动态性:进程是程序的一次执行过程,动态产生,动态消亡。
* 独立性:进程是一个能独立运行的基本单元。是系统分配资源与调度的基本单元。
* 并发性:任何进程都可以与其他进程并发执行。
2.并发与并行,同步与异步,阻塞与非阻塞
2.1并发与并行
并发
在操作系统中,某一时间段,几个程序在同一个CPU上运行,但在任意一个时间点上,只有一个程序在CPU上运行。
并行
当操作系统有多个CPU时,一个CPU处理A线程,另一个CPU处理B线程,两个线程互相不抢占CPU资源,可以同时进行,这种方式成为并行
并发与并行的区别
并发只是在宏观上给人感觉有多个程序在同时运行,但在实际的单CPU系统中,每一时刻只有一个程序在运行,微观上这些程序是分时交替执行。
在多CPU系统中,将这些并发执行的程序分配到不同的CPU上处理,每个CPU用来处理一个程序,这样多个程序便可以实现同时执行。
主要就是单CPU和多CPU的区别
2.2同步与异步
同步、
是指代码调用IO操作时,必须等待IO操作完成才能返回的调用方式。
异步
是指代码调用IO操作时,不必等待IO操作完成就能返回的调用方式。
2.3阻塞与非阻塞
阻塞
是指调用函数的时候当前线程被挂起。
非阻塞
是指调用函数的时候当前线程不会被挂起,而是立即返回。
3.线程
3.1引入
操作系统中拥有资源并独立运行的基本单位是进程,进程是资源的拥有者,进程的创建、撤销、切换花销太大。
多CPU处理出现,可以满足多个单位同时运行,但是多个进程并行花销太大。
后来出现了轻量级的,能够独立运行的基本单位,线程。
3.2什么是线程
线程是进程中的一个实体,是被系统独立调度和分派的基本单位。 线程的实体包括程序,数据,TCB。TCB包括:
线程状态
线程不运行时,被保存的现场资源
一组执行堆栈
每个线程的局部变量
访问统一进程中的资源
线程自己不拥有系统资源,只拥有一点运行中必不可少的资源。
同一进程中的多个线程并发执行,这些线程共享进程所拥有的资源。
3.3进程与线程的区别
· 进程是CPU资源分配的基本单位,线程是独立运行和独立调度的基本单位(CPU上真正运行的是线程)。
· 进程拥有自己的资源空间,一个进程包含若干个线程,线程与CPU资源分配无关,多个线程共享同一进程内的资源。
· 线程的调度与切换比进程快很多。
3.4python中thread使用
import threading,time
def thead(num):
# time.sleep(1)
print("线程%s开始执行"%num)
time.sleep(3)
print("线程%s执行完毕"%num)
def main():
print("主方法开始执行")
#创建2个线程
poll = []#线程池
for i in range(1,3):
thead_one = threading.Thread(target=thead, args=(i,))
poll.append(thead_one) #线程池添加线程
for n in poll:
n.start() #准备就绪,等待cpu执行
print("主方法执行完毕")
return
if __name__ == '__main__':
print(time.ctime())
num = main()
print("返回结果为%s"%num)
print(time.ctime())
4.协程
协程是一种比线程更加轻量级的存在,最重要的是,协程不被操作系统内核管理,协程是完全由程序控制的。
运行效率极高,协程的切换完全由程序控制,不像线程切换需要花费操作系统的开销,线程数量越多,协程的优势就越明显。
4.1协程,异步,并发之间的关系
协程与异步:协程并不是说替换异步,协程一样可以利用异步实现高并发。协程(coroutine)不等于异步(asynchonous),这种方法并不能将原来不支持异步的操作变成异步操作。
协程与并发:协程要利用多核优势就需要比如通过调度器来实现多协程在多线程上运行,这时也就具有了并行的特性。如果多协程运行在单线程或单进程上也就只能说具有并发特性。
4.2asyncio
4.2.1使用方式
asyncio的使用的方式是协程,具体的使用方法参见https://blog.csdn.net/Jordan_Lierge/article/details/107337600
4.2.2asyncio与await
根据官方文档说明,await 后面的对象必须是如下格式之一:
· A native coroutine object returned from a native coroutine function,一个原生 coroutine 对象。
· A generator-based coroutine object returned from a function decorated with types.coroutine(),一个由 types.coroutine() 修饰的生成器,这个生成器可以返回 coroutine 对象。
· An object with an await__ method returning an iterator,一个包含 __await__ 方法的对象返回的一个迭代器。
如果不满足上述三个要求之一,会抛出异常:
TypeError: object Response can't be used in 'await' expression
那如果想使普通方法变成awaitable的呢?
目前的办法只能去https://github.com/aio-libs也就是asyncio的库里面进行查找有没有合适的
4.2.3何时使用
如果是 I/O 密集型的, 并且I/O操作很慢,需要很多任务/线程协同实现, 使用asyncio
如果是 I/O 密集型的, 但是I/O很快,只需要有限数量的任务/线程,那么使用多线程即可
如果是 CPU 密集型的, 使用多进程。
IO密集型
IO密集型指的是系统的CPU性能相对硬盘、内存要好很多,此时,系统运作,
大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高。
CPU密集型
CPU密集型也叫计算密集型,指的是系统的硬盘、内存性能相对CPU要好很多,
此时,系统运作大部分的状况是CPU Loading 100%,CPU要读/写I/O(硬盘/内存),
I/O在很短的时间就可以完成,而CPU还有许多运算要处理,CPU Loading很高。
4.3asyncio中的run_until_complete
4.3.1事件循环Event loop
事件循环的作用是管理所有的事件,在整个程序运行过程中不断循环执行,追踪事件发生的顺序将他们放到队列中,
当主线程空闲的时候,调用相应的事件处理者处理事件。
4.3.2 协程与事件循环
import asyncio
import time
async def do_some_work(x): # async关键字定义一个协程
print('Waiting:', x)
coroutine = do_some_work(2) # 协程的调用不会直接执行,而是返回一个协程对象
loop = asyncio.get_event_loop() # 创建一个事件循环
loop.run_until_complete(coroutine) # run_until_complete将协程注册到事件循环,并启动事件循环。
协程对象是不能直接运行的,在注册事件循环的时候,run_until_colplete
将协程封装成了一个task
, 这个task
对象是Future
类的一个子类,它保存了协程运行后的状态,用于在未来获取协程的结果。
import asyncio
import time
async def do_some_work(x): # async关键字定义一个协程
print('Waiting:', x)
coroutine = do_some_work(2) # 协程的调用不会直接执行,而是返回一个协程对象
loop = asyncio.get_event_loop() # 创建一个事件循环
task = loop.create_task(coroutine) # 创建task,此时的task尚未加入事件循环,状态为pending
loop.run_until_complete(task) # run_until_complete将task注册到事件循环,并启动事件循环。
# task执行完后,状态为finished
run_until_complete
所接收的参数是一个future对象,它接收以下两种:
task
,task是Future的子类。asyncio.ensure_future(coroutine)
和loop.create_task(coroutine)
都可以创建task,推荐使用asyncio.ensure_future(coroutine)
- 协程,如果接收的是一个协程的话,会自动将其封装成
task
# 绑定回调函数
import asyncio
import time
async def do_some_work(x): # async关键字定义一个协程
print('Waiting:', x)
return 'Done after {}s'.format(x) # 返回值
def callback(future): # 定义一个回调函数,最后一个参数是future对象,如果这里有多个参数,下方通过偏函数导入
print('Callback: ', future.result()) # 返回future的结果
coroutine = do_some_work(2) # 协程的调用不会直接执行,而是返回一个协程对象
loop = asyncio.get_event_loop() # 创建一个事件循环
task = loop.create_task(coroutine) # 创建task,此时的task尚未加入事件循环,状态为pending
task.add_done_callback(callback) # 绑定回调函数,在task执行完毕的时候获取执行的结果
loop.run_until_complete(task) # run_until_complete将task注册到事件循环,并启动事件循环。
# task执行完后,状态为finished
coroutine执行结束时会调用回调函数,并通过参数future获取协程执行的结果,此future是没有传入的,实际上与创建的task是同一个对象
# 阻塞和await
import asyncio
import time
now = lambda: time.time()
async def do_some_work(x):
print('Waiting: ', x)
await asyncio.sleep(x) # 执行sleep时,await使此协程主动让出控制权,loop调用其他协程
return 'Done after {}s'.format(x)
start = now()
coroutine = do_some_work(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)
loop.run_until_complete(task)
print('Task ret: ', task.result())
print('TIME: ', now() - start)
# 这里还只有一个协程,让出控制权也暂时无用,如果有多个协程,协程A让出控制权,loop就会把控制权分配给协程B,实现了并发。
实现并发:
# asyncio实现并发:每当一个协程任务阻塞的时候就await,让后其他协程继续工作。
import asyncio
import time
now = lambda: time.time()
async def do_some_work(x):
print('Waiting: ', x)
await asyncio.sleep(x)
return 'Done after {}s'.format(x)
start = now()
coroutine1 = do_some_work(1)
coroutine2 = do_some_work(2)
coroutine3 = do_some_work(4)
tasks = [
asyncio.ensure_future(coroutine1),
asyncio.ensure_future(coroutine2),
asyncio.ensure_future(coroutine3)
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks)) # asyncio.wait(接收task列表);asyncio.gather(接收多个task)
for task in tasks:
print('Task ret: ', task.result())
print('TIME: ', now() - start)
小结
run_until_complete执行完成以后,会调用loop.stop(),其实这样是有风险的,因为loop会停止,有可能导致整个服务停掉。