进程、线程、协程的区别 GIL(全局解释性锁): 无论启多少个线程,有多少个cpu,Python在执行的时候会在同一时刻只允许一个线程运行。 一个线程需要执行任务,必须获取GIL。 好处:直接杜绝了多个线程访问内存空间的安全问题。 坏处:Python的多线程不是真正多线程,不能充分利用多核CPU的资源。 线程锁(互斥锁): 线程锁保证同一时刻只有一个线程修改内存空间的同一数据,GIL保证同一时刻只有一个线程在运行。 多线程同时修改同一数据,可能会导致数据不准确既线程不安全。 进程: 系统资源分配的最小单位,需要自己独立的内存空间,进程间数据不共享,开销大,效率低。 可以实现并行(进程数小于CPU数的情况下)和并发。 稳定性好,一个子进程崩溃不会影响其他进程。 若进程过多,操作系统调度会出现问题。 适合密集CPU计算业务。 线程: 依赖进程存在,多个线程之间数据共享、全局变量共享,相对进程效率更高。 因为GIL的存在,在同一进程里只能实现并发。 如果需要保证线程安全,需要加锁控制(锁会降低效率)。 相对进程效率高,适合IO密集型的多任务操作。 协程: 又称微线程,拥有自己的寄存器上下文和栈,协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈几乎没有内核的切换开销。 可以不加锁访问全局变量。 数量上可以是无限个,和多线程比,线程数量越多,协程的性能优势就越明显。 不需要原子操作锁定及同步的开销。 高并发、高扩展性、低成本、效率高。 处理网络I/O性能比较高。 处理CPU计算密集型的时候,性能较低。 本质是单线程,不能同时利用CPU多核,需要和进程配合才能运行在多CPU上,只能实现并发。 打个比方来说明进程、线程、协程的问题: 假设有一个操作系统,是单核的,系统上没有其他的程序(保证单进程)运行,有两个线程 A 和 B ,A 和 B 在单独运行时都需要 10 秒来完成自己的任务,而且任务都是运算操作,A B 之间也没有竞争和共享数据的问题。现在 A B 两个线程并行,操作系统会不停的在 A B 两个线程之间切换,达到一种伪并行的效果,假设切换的频率是每秒一次,切换的成本是 0.1 秒(主要是栈切换),总共需要 20 + 19 * 0.1 = 21.9 秒。如果使用协程的方式,可以先运行协程 A ,A 结束的时候让位给协程 B ,只发生一次切换,总时间是 20 + 1 * 0.1 = 20.1 秒。如果系统是双核的,那么 A B 两个线程就可以真并行(开启两个进程),总时间只需要 10 秒,而协程的方案仍然需要 20.1 秒。 单线程和多线程的效率问题 在运行程序的过程中,主要是处理网络IO、CPU计算、磁盘IO几种情况。 执行网络IO操作 python解释器在遇到网络IO操作阻塞时会自动释放GIL供其他线程获取。如果是遇到网络IO的操作,因为python解释器会自动释放锁,网络IO过程中本就有些延迟,此时多线程的效率会根据网络延迟情况比单线程高很多。 执行纯计算操作 如果是纯计算的程序,没有网络IO操作,python解释器会每隔100次(次数可以通过sys.setcheckinterval调整)操作释放这把锁。 python在想要执行某个线程的前提是必须拿到GIL这把锁才能进入CPU执行,每次释放GIL,线程进行锁竞争、切换线程,会消耗资源,但python里一个进程永远只能执行一个线程,所以无论CPU的核数是几核,此时python的多线程和单线程相比效率并不高,改成C语言操作此类计算效果会比较好。 使用python创建进程、线程、协程demo python通过多进程实现多任务demo 方法一:使用multiprocessing模块: 创建Process的实例 import multiprocessing import time def task1(): while True: time.sleep(1) print("I am task1") def task2(): while True: time.sleep(2) print("I am task2") if __name__ == '__main__': p1 = multiprocessing.Process(target=task1) # multiprocessing.Process创建了子进程对象p1 p2 = multiprocessing.Process(target=task2) # multiprocessing.Process创建了子进程对象p2 p1.start() # 子进程p1启动 p2.start() # 子进程p2启动 print("I am main task") # 这是主进程的任务 方法二:使用进程池Pool import multiprocessing import time def task1(): while True: time.sleep(1) print("I am task1") def task2(): while True: time.sleep(2) print("I am task2") if __name__ == '__main__': pool = multiprocessing.Pool(processes=2) # 创建包含2条进程的进程池,使用池可以有效控制进程池的最大数量 pool.apply_async(task1) # 实现异步 pool.apply_async(task2) pool.close() # 关闭进程池。在调用该方法之后,该进程池不能再接收新任务,它会把当前进程池中的所有任务执行完成后再关闭自己。 pool.join() # 等待所有进程完成。 python通过多线程实现多任务demo import threading from time import sleep def task1(): # 线程函数1 for i in range(0, 9): print("I am task1") def task2(): # 线程函数2 print('I am task2') sleep(1) t1 = threading.Thread(target=task1) # 线程一 t2 = threading.Thread(target=task2) # 线程二 t1.start() # 开始线程一 # t1.join() # 线程等待,程序会停留在这里,等线程一执行完之后再继续执行下面的代码。 t2.start() # 开始线程二 python通过协程实现多任务demo 方法一:使用gevent创建协程 import gevent from gevent import monkey import time import random # 有耗时操作时需要 monkey.patch_all() # 将程序中用到的耗时操作代码,换为 gevent 中自己实现的模块 def task1(): for i in range(10): print('I am task1', i) time.sleep(random.random()) def task2(): for i in range(10): print('I am task2', i) time.sleep(random.random()) gevent.joinall([ # 将协程任务添加到事件循环,接收一个任务列表 gevent.spawn(task1), # 创建一个普通的执行单元对象并切换 gevent.spawn(task2) ]) # 等价于 ''' g1 = gevent.spawn(work, 'work1') g2 = gevent.spawn(work, 'work2') g1.join() g2.join() ''' 方法二:使用asyncio创建协程 import asyncio @asyncio.coroutine # 把一个generator(生成器)标记为coroutine类型,然后把这个coroutine扔到EventLoop中执行。 def task1(): for i in range(10): yield from asyncio.sleep(1) # yield from 调用另外一个generator 然后sleep 1秒,模拟IO延迟 print('I am task1') @asyncio.coroutine def task2(): for i in range(10): yield from asyncio.sleep(1) print('I am task2') loop = asyncio.get_event_loop() # 获取EventLoop,相当于一个循环 tasks = [task2(), task1()] function(){ //外汇返佣 http://www.fx61.com/ loop.run_until_complete(asyncio.wait(tasks)) # 执行EventLoop中的coroutine loop.close() python使用多进程和协程结合实现多任务demo from multiprocessing import Pool import gevent def task1(): for i in range(10): gevent.sleep(2) print('I am task1') def task2(): for i in range(10): gevent.sleep(2) print('I am task2') def coroutine(): gevent.joinall([ gevent.spawn(task1), gevent.spawn(task2) ]) if __name__ == "__main__": p = Pool() # 不加数字,默认为当前CPU核数 for i in range(3): p.apply_async(coroutine, args=()) p.close() p.join() |