如何使用线程池
解决方法:Python3中有线程池的实现。使用标准库中concurrent.futures下的ThreadPoolExecutor,对象的submit和map方法可以用来启动线程池中线程执行任务
from concurrent.futures import ThreadPoolExecutor
executor=ThreadPoolExecutor(3) ##创建一个Executor对象,指定线程池中有几个线程
##使用线程池中线程执行任务
def f(a,b):
print('f',a,b)
return a**b
executor.submit(f,2,3)
##调用线程池中线程执行函数,传入函数,及相应参数
##函数运行结束后,线程归还到线程池,并返回future对象
future.result()##获取线程执行函数的结果
executor.map(f,[2,3,4],[4,5,6])
##在多个线程上同时调用函数,参数传入列表
##当线程池中所有线程都在忙碌,再传入一个任务则要等待一个空闲线程才会运行
如何使用多进程
场景:由于python中GIL的存在,再任意时刻只允许一个线程再解释器中运行。因此python的多线程不适合处理cpu密集型的任务。
想要处理cpu密集型任务,可以采用多进程模型
ps:多个进程则有多个GIL锁,一个进程的多个线程只能一个一个执行
解决方法:使用标准库中multiprocessing.Process 它可以启动子进程执行任务
操作接口,进程间通信,进程间同步等,与Threading.Thread类似
from multiprocessing import Process
def f(s):print (s)
Process(target=f,args=('hello',))
p.start() ##启动进程
p.join() #等待进程的结束
x=1
def g():
global x
x=5
p=Process(target=f)
p.start()
x
>1 ##可见子进程和主进程是独立的,x是不一样的
##进程间如何进行通信
from multiprocessing import Queue,Pipe
q=Queue()
def f(q):
print('start')
print(q.get()) ##获取队列的值
print('end')
Process(target=f,args=(q,)).start()
>start
q.put(100)
>100
>end
c1,c2=Pipe() ##创建双向的管道
c1.send('abc') ##由c1端写入的数据要从c2端读取
c2.recv()##同理,由c2端写入的数据要从c1端读取
def f(c):
c.send(c.recv()*2)
c1,c2=Pipe()
Process(target=f,args=(c2,)).start()
c1.send(55) ##执行了c.recv()*2
c2.recv() ##执行了c.send()
>110
科普:水仙花数(Narcissistic number)也被称为阿姆斯特朗数(Armstrong number),水仙花数是指一个 3 位数,它的每个位上的数字的 3次幂之和等于它本身(例如:1^3 + 5^3+ 3^3 = 153)。
##cpu密集型任务:多进程多线程,寻找水仙花数
from threading import Thread
from multiprocessing import Process
def isArmstrong(n): ##判断水仙花数
a,t=[].n
while t>0:
a.append(t%10)
t /=10
k=len(a)
return sum(x**k for x in a)==n
def findArmstrong(a,b): ##在某一范围查找水仙花数
print(a,b)
res=[k for k in range(a,b) if isArmstrong(k)]
print('%s-%s:%s'%(a,b,res)
def findByThread(*argslist): ##使用多线程
workers=[]
for args in argslist:
worker=Thread(target=findArmstrong,args*=args)
workers.append(worker)
worker.start()
for worker in workers:
worker.join()
def findByProcess(*argslist):
workers=[]
for args in argslist:
worker=Process(target=findArmstrong,args=args)
workers.append(worker)
worker.start()
for worker in workers:
worker.join()
if __name__=='__main__':
import time
start=time.time()
findByProcess((2000000,2500000),(2500000,300000)) ##进程花费15秒多
findByThread((2000000,2500000),(2500000,300000))
##cpu利用不到100%,线程花费40秒多
print(time.time()-start)
如何使用函数装饰器
场景:为多个函数统一添加某种功能,不用一一添加相同代码。比如计时统计,记录日志,缓存运算结果等等
解决方案:定义装饰器函数,用它来生成一个在原函数基础添加了新功能的函数,代替原函数
##设计一个装饰器解决重复计算子问题的例子(减少重复计算):
##用缓存记录之前的计算结果,在每一次计算前检查以前的运算是否可以拿来利用
def memo(func):
cache={}
def wrap(*args):
if args not in cache:
cache[args]=func(*args)
return cache[args]
return wrap
##题目一:斐波那契数列:指的是1,1,2,3,5,8,13,21...
##这个数列从第三项开始,每一项都等于前两项之和,求数列第n项
@memo ##等价于fibonacci=memo(fabonacci)
def fibonacci(n):
if n <= 1:
return 1
return fibonacci(n-1)+fibonacci(n-2)
##题目二:一个共有10个台阶的楼梯,从下面追到上面,一次只能迈
##1-3个台阶,并且不能后退,走完这个楼梯共有多少种方法。(回溯法问题)
@memo
def climb(n,step):
count=0
if n==0:
count=1
elif n>0:
for step in steps:
count+=climb(n-step,steps)
return count
climb(10,(1,2,3))
如何为被装饰的函数保存元数据
在函数对象中保存这一些函数的元数据,例如
f.__name__:函数的名字
f.__doc__:函数文档字符串
f.__moudle__:函数所属模块名
f.__dict__:属性字典
f.__defaults__:默认参数元组
我们在使用装饰器后,再使用上面这些属性访问时,看到的是内部包裹函数的元数据,原来函数的元素据便丢失掉了
解决方法:使用标准库functools中的装饰器wraps装饰内部包裹函数,可以 制定将原函数的某些属性,更新到包裹函数上面
def f():
'''f function''' ##定义文档字符串
a=2
return a*2
##举例
f.__name__
>'f'
f.__doc__
>'f function'
f.__module__
>'__main__'
def mydecirator(func):
def wrapper(*args,**kargs):
'''wrapper function'''
print('In wrapper')
func(*args,**kargs)
return wrapper
## @mydecorator
def example():
'''example function'''
print('In example')
##在没有装饰器的情况下
example.__name__
>'example'
example.__doc__
>'example function'
##在有装饰器的情况下
##example.__name__
##>'wrapper'
##example.__doc__
##>'wrapper function'
##修改后
from functools import updata_wrapper,wraps
def mydecirator(func):
##@wraps(func) ##最简单,直接使用装饰器
def wrapper(*args,**kargs):
'''wrapper function'''
print('In wrapper')
func(*args,**kargs)
updata_wrapper(wrapper,func,('__name__','__doc__'),('__dict__',))
##将原函数的属性更新到包裹函数当中
##参数分别是包裹函数,被包裹函数,替换的元数据,合并的元数据
##后两项参数的默认值分别为('__module__'__name__','__doc__'),('__dict__')
return wrapper
@mydecorator
def example():
'''example function'''
print('In example')
example.__name__
>'example'
example.__doc__
>'example function'