- 进程:就是程序-软件
- 一个进程就相当于一个任务,进程与进程之间互不干涉
- 当一个任务被开启后,操作系统会分配它所需的系统资源,包括内存,CPU等,如果系统资源不够,则会出现系统崩溃,这样的任务可以被称为进程
- 进程需要口粮:CPU和内存,当资源不足时,系统可能会奔溃
- 多进程是一种并行的模式,不同于单进程(串行),多进程的效率更高
- 多进程在不同的内核跑道中执行叫做多进程的并行处理
- 线程是执行程序的最小单元
- 进程提供线程执行程序的前置要求,线程在充足的资源配置下,执行程序
- 线程是操作系统最小执行单元,一个进程至少由一个线程组成
- 进程被运行后算一个线程,进程是不运行的,线程才会运行,而一个进程有多个线程就涉及到进程有多少可以被cpu单独调用的模块,这个调用的模块可以通过手动创建线程来建立
- 进程是吸收资源的,而线程是干活的。
- 进程间不可以共享内存,而线程却可以
- 线程可手动创建或者撤销另一个线程
- 进程需要引用第三方模块multiprocessing ,调用方式为multiprocessing.process(target='')
- 线程需要引用第三方模块threading,调用方式为threading.Thread(trager='',name='')
- 一个核心中在多个时间片上同时处理线程的行为叫做并发执行
- 多个CPU之间同时执行叫并行;单个CPU内核中多个线程同时工作叫并发;不过一般工作中都会叫并发统称,名词上的统称,不用纠结
import time,os import multiprocessing def work_a(): for i in range(3): print(i,'a',os.getpid()) # print('对应进程ID',os.getpid()) time.sleep(1) def work_b(): for i in range(3): print(i,'b',os.getpid()) time.sleep(1) def work_c(): for i in range(3): print(i,'c',os.getpid()) time.sleep(1) if __name__=="__main__": start = time.time() # 主进程 c_p = multiprocessing.Process(target=work_c)# 子进程c c_p.start()# 子进程c执行 c_p.join() a_p = multiprocessing.Process(target=work_a)# 子进程a a_p.start() # 子进程a执行 # work_a() # work_b() # # 可以使用for循环减少代码 # for p in (c_p,a_p): # p.start() # # # 使用join阻塞,可以让主进程等子进程全部执行完之后再执行 # for p in (c_p,a_p): # p.join() for p in (c_p,a_p): print(p.is_alive()) print('主进程时间消耗',time.time()-start) # 主进程 print('主进程ID',os.getpid())
- 进程池与进程锁
- 一、创建进程锁
在进程代码中需要加上锁的地方写加锁代码,要释放锁的地方写解锁代码既可:
使用模块:multiprocessing
如何加锁:multiprocessing.Manager().Lock().acquire()
如何解锁:multiprocessing.Manager().Lock().release()
创建进程池:先写出创建进程池的方法,之后往进程池中放入进程即可
使用的模块:multiprocessing
创建的方法:multiprocessing.Pool(...)
二、使用进程池的优势a
不仅能制定进程创建的数量,还可以避免进程的创建于关闭带来的消耗
pool = multiprocessing.Pool(4)
manager = multiprocessing.Manager()
lock = manager.Lock()
lock.acquire()
import time,os import multiprocessing def work_a(): for i in range(3): print(i,'a',os.getpid()) # print('对应进程ID',os.getpid()) time.sleep(1) def work_b(): for i in range(3): print(i,'b',os.getpid()) time.sleep(1) def work_c(): for i in range(3): print(i,'c',os.getpid()) time.sleep(1) if __name__=="__main__": start = time.time() # 主进程 c_p = multiprocessing.Process(target=work_c)# 子进程c c_p.start()# 子进程c执行 c_p.join() a_p = multiprocessing.Process(target=work_a)# 子进程a a_p.start() # 子进程a执行 # work_a() # work_b() # # 可以使用for循环减少代码 # for p in (c_p,a_p): # p.start() # # # 使用join阻塞,可以让主进程等子进程全部执行完之后再执行 # for p in (c_p,a_p): # p.join() for p in (c_p,a_p): print(p.is_alive()) print('主进程时间消耗',time.time()-start) # 主进程 print('主进程ID',os.getpid()) # var1 = multiprocessing.Process()
import multiprocessing import os import time def work(count): print(str(count)+'a',os.getpid()) time.sleep(1) def work_b(count): print(str(count) + 'b', os.getpid()) time.sleep(1) return 'work_b返回值 %s pid:%s'% (count,os.getpid()) def work_c(count,lock): lock.acquire() print(str(count) + 'b', os.getpid()) time.sleep(1) lock.release() return 'work_b返回值 %s pid:%s'% (count,os.getpid()) if __name__=='__main__': pool = multiprocessing.Pool(4) manager = multiprocessing.Manager() lock = manager.Lock() # for i in range(20): # pool.apply_async(func=work,args=(i,)) # args是个元组,单个参数时记得加, # pool.close() #实际生产中,有的进程需要一直运行着,就不用关闭 # pool.join() # 和close一起用 # results = [] # for i in range(20): # # apply_async 异步可以有返回值 # pool_re = pool.apply_async(func=work_b,args=(i,)) # results.append(pool_re) # # for res in results: # print(res.get()) # print(len(results),results) for i in range(20): pool.apply_async(func=work_c,args=(i,lock)) # print('====') # time.sleep(20) pool.close() #实际生产中,有的进程需要一直运行着,就不用关闭 pool.join() # 和close一起用
进程之间的通信:
import json import multiprocessing import time # 有问题,没解决,采用直接函数调用试试 class Work(object): def __init__(self,q): self.q = q # print(self.q, type(self.q)) def send(self,message): print(self.q,type(self.q)) try: # 如果不是字符串格式的,要json序列化为字符串格式在传输 # json.dumps(obj)对象序列化为字符串 # 序列化:对象信息 / 数据结构信息,转化为字符串 if not isinstance(message,str): message = json.dumps(message) except Exception as e: print(e) self.q.put(message) def receive(self): while 1: result = self.q.get() try: res = json.loads(result) except: res = result print('receive massage is %s'% res) def all_send(self): for i in range(10): self.q.put(i) time.sleep(1) if __name__ == '__main__': q = multiprocessing.Queue() work = Work(q) send = multiprocessing.Process(target=work.send,args=({'name':'xiaoming'},)) recv = multiprocessing.Process(target=work.receive) all_send_p = multiprocessing.Process(target=work.all_send) all_send_p.start() send.start() recv.start() all_send_p.join() # 只需要阻塞最长使用时间的进程 recv.terminate() #正常退出接收方
队列02:
import json import multiprocessing import queue import random import threading import time # 线程 def sender(): # print(q,type(q)) while 1: x = random.randint(1,10) print('send num:',x) q.put(x) # 往队列中放入数据 time.sleep(1) # if x+5>14: # return def recvder(): while 1: result = q.get() # 从队列中取数据 # try: # res = json.loads(result) # except: # res = result print('receive num:%s'% result*3) time.sleep(1) # if result == 10: # break # def all_send(q): # for i in range(10): # q.put(i) # time.sleep() if __name__ == '__main__': q = queue.Queue() # 定义队列 send = threading.Thread(target=sender) recv = threading.Thread(target=recvder) # all_send_p = multiprocessing.Process(target=all_send) # all_send_p.start() send.start() recv.start() # send.join() # recv.join() # all_send_p.join() # 只需要阻塞最长使用时间的进程
- 多线程运行时,可能出现的问题及解决方案:
1、通过线程执行的函数无法获取返回值——【解决方案】线程间如何通信:通过队列
2、 多个线程同时修改文件可能造成数据错乱。——解决方案】线程间如何避免资源抢占:创建线程锁
3、线程数量太多可能会造成资源不足,甚至死机等情况。——【解决方案】如何避免创建线程数量过多:线程池
- 通过队列通信解决
- 创建线程锁
- 使用模块:threading
- 如何加锁:threading.Lock().acquire()
- 如何解锁:threading.Lock().release()
- 创建线程池
- 使用模块:concurrent.futures
- 创建方法:concurrent.futures.ThreadPoolExcutor()
import random import threading import time books = ['python','flask','C++','java','flutter', 'golang','spring boot'] new_list = [] def work(): if len(books) == 0: return else: data = random.choice(books) _data = '%s_new'% data new_list.append(_data) books.remove(data) time.sleep(1) if __name__=='__main__': start_time = time.time() # for i in range(len(books)-3): # work() t_list = [] # 使用多线程缩短运行时间 for i in range(len(books)-3): t = threading.Thread(target=work) t_list.append(t) t.start() # for t in t_list: # t.join() print('old list:',books) print('new list:',new_list) print('时间花费为 %s'%(time.time()-start_time)) 线程池
import os import time from concurrent.futures import ThreadPoolExecutor import threading # 使用线程锁时,只用在全局定义线程锁即可,不需要传入函数中,作为参数加锁 # 进程,需要放入函数中,进行加锁 lock = threading.Lock() # 这个才是<class '_thread.lock'> lock1 = threading.Lock # <built-in function allocate_lock> <class 'builtin_function_or_method'> def work_a(i): lock.acquire() print(i,'a',os.getpid()) time.sleep(1) lock.release() def work_b(i): print(i,'b',os.getpid()) time.sleep(1) return 'result is %s a'% i if __name__=='__main__': print(lock,type(lock)) print(lock1,type(lock1)) tpool = ThreadPoolExecutor(2) # for i in range(10): # tpool.submit(work_a,(i,)) # print('=======') # 线程值 获取返回值work_b result = [] for i in range(10): t_result = tpool.submit(work_b,(i,)) result.append(t_result) time.sleep(10) for res in result: print(res,res.result(),'res') # res.result()获取当前线程执行任务的结果 print('----',os.getpid())
- 进程锁与线程锁的区别
- 使用线程锁时,只用在全局定义线程锁即可,不需要传入函数中,作为参数加锁
- 进程,需要放入函数中,进行加锁
- 全局锁GIL(python解释器自带的) 使得python的多线程只能在单一cpu上工作,目的是为了保证线程的安全
- 解决方式:pypy解释器(不太推荐),多进程+多线程:通过多个进程在每个CPU跑道上,每个跑道上再执行多线程,去到各自的cpu时间片上执行
- 什么是全局锁
python有全局锁gil,多线程只能在单一cpu工作。可以用多进程+多线程配合使用
python无法在多条跑道执行任务的主要原因就是因为GIL全局锁(初步理解没有去掉GIL锁的原因是:线程安全)
全局锁的主要作用
因为多线程的编程方式,使得线程之间的数据的一致性和状态同步难以把控,为了解决数据不能同步的问题,设计了GIL全局解释器锁。
全局锁是如何发挥作用的
在Cpython解锁器中,当python代码有一个线程开始访问解释器的时候,GIL会把这个线程给锁上,此时此刻其他的线程只能干等着,无法对解释器的资源进行访问,需要等这个线程分配的时间到了,这个线程把GIL释放掉,另外的线程才开始跑起来,其实这无疑也是一个单线程。这类似于给线程加锁threading.Lock(),解锁threading.Lock().release()一样。
# 异步 async,await,asyncio,gevent
- 异步:轻量级的线程—协程;可以获取异步函数返回值
- 多进程和多线程,可以随时创建使用(虽然多进程每一次创建都会消耗一些CPU资源),但是异步有要求,主进程必须是异步的情况下才可以使用,并且执行过程中所有的程序都是异步才行
- 因为异步有返回值,所以更适合类似于文件读写(需要返回值的业务),而多进程和多线程更适合业务处理,不需要返回值的工作
- 多进程,多线程,异步,都是通过函数来处理,异步函数使用async 定义,await执行异步
- gevent python2就出现了,不同环境用不同的,
- 查看错误信息,然后解决
- 异步总结
- 相对于同步而言,异步意味着无序。正因为异步的无序,使得各个程序间的协调成为一大难题
- 异步编程就是为了解决这一问题的编程方式,它以进程、线程、协程、函数/方法作为执行任务程序的基本单位,结合回调、事件循环、信号量等机制,以提高程序整体执行效率和并发能力的编程方式
- 关于异步 记住三对
- 关键词: async 定义声明 ,await main()中调用执行
- 模块:asyncio(调用异步函数) 加入asyncio.gather() 执行asyncio.run()
- 第三方模块:gevent异步包
- 创建协程对象gevent.spwan() 批量处理协程对象 gevent.joinall()
import os import random import time import asyncio import gevent def gevent_a(): for i in range(5): print(i,'a-gevent',os.getpid()) gevent.sleep(random.random()*2) return 'gevent a result' def gevent_b(): for i in range(5): print(i,'b-gevent',os.getpid()) gevent.sleep(random.random()*2) return 'gevent b result' def work_a(): for i in range(10): print(i,'a') time.sleep(random.random()*2) return 'a-func' def work_b(): for i in range(10): print(i,'b') time.sleep(random.random()*2) return 'b-func' async def work_c(): for i in range(10): print(i,'c',os.getpid()) await asyncio.sleep(random.random()*2) return 'c-func' async def work_d(): for i in range(10): print(i,'d',os.getpid()) await asyncio.sleep(random.random()*2) return 'd-func' # time.sleep()是CPU级别的阻塞,他会阻塞异步效果 # 解决方法,time.sleep()更换为asyncio.sleep() # asyncio.sleep()和 gevent.sleep()业务级别的阻塞 # 调用异步函数,需要定义异步函数主函数 # 用asyncio.gather()调用批量执行,await声明执行 async def main(): result = await asyncio.gather( work_d(), work_c() ) print(result) print(result[0],result[1]) if __name__== '__main__': start = time.time() # work_a() # work_b() # asyncio.run(main())#执行主异步函数 g_a = gevent.spawn(gevent_a) # 创建协程对象 g_b = gevent.spawn(gevent_b) print(g_a,type(g_a)) gevent_list = [g_a,g_b] # 也可以用g_b.run()或者g_b.start()启动 result = gevent.joinall(gevent_list)#批量处理协程对象 print('===',result) print(dir(result[0])) print(result[0].value) print(result[1].value) print(time.time()-start) print('主进程',os.getpid())