1.进程与进程池的效率对比
- 多进程:p.start()过程中,只是向操作系统发送一个信号,至于什么时候执行,都是操作系统的事情,操作系统接收到信号时,帮该进程申请一块内存空间+拷贝父进程的地址空间
#多进程执行效率 from multiprocessing import Process import time def func(i): sum = 0 time.sleep(1) sum += i print(sum) if __name__ == '__main__': ls = [] statt = time.time() for i in range(10): p = Process(target=func,args=((i,))) p.start() ls.append(p) [p.join() for p in ls] print(time.time() - statt) #执行时间 1.3582112789154053
- 进程池:
一个池子,里边有固定数量的进程。这些进程一直处于待命状态,一旦有任务来,马上就有进程去处理。
因为在实际业务中,任务量是有多有少的,如果任务量特别的多,不可能要开对应那么多的进程数
开启那么多进程首先就需要消耗大量的时间让操作系统来为你管理它。其次还需要消耗大量时间让
cpu帮你调度它。
进程池还会帮程序员去管理池中的进程。
#进程池的效率 from concurrent.futures import ProcessPoolExecutor from multiprocessing import Process import time def func(i): sum = 0 time.sleep(1) sum += i return sum ls = [] if __name__ == '__main__': p = ProcessPoolExecutor(4) start = time.time() for i in range(10): #向进程池中丢任务,这十个发送的信号会被直接添加到列表中 #至于你执行不执行,那是操作系统的事,这时列表中就有了十个返回值对象 obj = p.submit(func,i) ls.append(obj) #任务对象会在ls列表中依次排好顺序,然后p.result()会依次等待结果! [print(p.result()) for p in ls] print(time.time() - start) #上面列表等待的话,这里会一瞬间完成,因为上面代码中每个任务都已经执行结束 p.shutdown(wait=True) #时间效率 3.124636650085449
- 总结:
如果在机器可承受范围内,进程数开的越多,执行效率越快!
1.轻量级的任务多进程和进程池的执行效率相差不大;因为在开多进程时,开启进程的时间
和cpu切换的时间可以忽略不计!
2.任务量较大时,因为进程池里已经有开启好的进程,随时可以调度,节省了多进程开启进程的时间
和资源利用率!
2.线程与进程对比
- 线程:线程被称为轻量级的进程,好比一个车间的一条流水线,是可执行的基本单位,是可被调度的基本单位,线程不可以自己独立拥有资源,只有开启进程了,才有线程的概念,必须依赖于所属进程中的资源;所以线程中没有父子关系,因为大家用的都是进程的资源,都是进程创建出来的;
- 进程:是一个执行过程,是一个资源分配的基本单位,进程与进程之间是独立的,主进程与子进程之间是相互隔离的,每一个进程默认都有一个控制线程,该线程可以执行代码,从而创建新的线程;
- 线程分为用户级线程和内核级线程:
- 用户级线程:对于程序员来说的,这样的线程完全被程序员控制执行,调度
- 内核级线程:对于计算机内核来说,这样的线程完全被内核控制调度
- 进程由 代码段 数据段 PCB组成(process control block)
- 线程由 代码段 数据段 TCB组成(thread control block)
- 线程分为用户级线程和内核级线程:
n = 100 def func(): global n n = 0 if __name__ == '__main__': p = Thread(target=func,) p.start() print('线程的n值:%s'% n) #线程的n值:0 所以线程是共用了进程的资源,
n = 100 def func(): global n n = 0 if __name__ == '__main__': p = Process(target=func,) p.start() print('线程的n值:%s'% n) #输出结果 线程的n值:100 进程是相互空间隔离的
3.线程与线程池
- 回调函数:线程池的回调函数是由主进程调用的,子进程只负责把结果传递给回调函数,回调函数通过result()取得对象,再进行进一步的操作;
- 线程的回调函数是子线程调用,而不是主线程;
- 线程池跟进程池的概念一样,它的开启都不是无限的,默认也是内核乘以5;
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor import requests from threading import current_thread def func(url): respons = requests.get(url) if respons.status_code == 200: return {'url':url,'text':respons.text} def call_back(obj): #回调函数: #1.在主线程中可以等你运行完了,统一对线程result取得结果 #2.但是你还得等待每一个线程运行结束,而回调函数,是每一个线程附带一个函数,一旦运行结束 #就将对象传给回调函数,回调函数result()取得子线程的返回值 res = obj.result() print('[%s] <%s> (%s)' % (current_thread().getName(), res['url'], len(res['text']))) if __name__ == '__main__': url = [ 'https://www.bilibili.com', 'https://www.baidu.com', 'https://www.tmall.com', 'https://www.jd.com' ] p = ThreadPoolExecutor(5) for i in url: p.submit(func,i).add_done_callback(call_back) p.shutdown(wait=True) #输出结果 [ThreadPoolExecutor-0_1] <https://www.baidu.com> (2443) [ThreadPoolExecutor-0_0] <https://www.bilibili.com> (28253) [ThreadPoolExecutor-0_2] <https://www.tmall.com> (216218) [ThreadPoolExecutor-0_3] <https://www.jd.com> (107838)