第一部分:Python
一.并发编程
1.概述:
非并发:程序由单个步骤序列构成,独立子任务的执行性能低。
并发:异步、高效,分解子任务,简化流程与逻辑。
进程process:一个程序的执行实例,有自己的地址空间、内存、数据栈及辅助数据。
线程thread:同一进程内,可被并行激活的控制流,共享相同空间地址、数据结构,但线程的访问顺序差异会导致结果不同(race condition竞赛条件)。
python GIL:全局解释器锁Global Interpreter Lock,python代码由虚拟机(解释器主循环)控制,主循环同时只能有一个控制线程执行。
2._thread模块(不推荐使用):没有控制进程结束机制,只有一个同步原语(锁),功能少于threading模块。
_thread.start_new_thread(函数名,存放参数的元组)
3.threading模块:
1) .Thread线程类,构造时threading.Thread(target=目标函数,args=(参数,)),类中的函数:
.start()----启动线程
.join()----要求主线程等待
.name
2) .current_thread()----获取当前线程
import time
import threading
def worker(n):
print('{}函数执行开始于:{}'.format(threading.current_thread().name,time.ctime()))
time.sleep(n)
print('{}函数执行结束于:{}'.format(threading.current_thread().name,time.ctime()))
class MyThread(threading.Thread):
def __init__(self,function,args):
threading.Thread.__init__(self)
self.function = function
self.args = args
#需要重新定义.start()函数中调用的run()函数
#args参数为元组形式,故函数调用时需加*
def run(self):
self.function(*self.args)
def main():
print('【主函数执行开始于:{}】'.format(time.ctime()))
threads = []
#创建线程序列
t1 = MyThread(worker,(3,))
t2 = MyThread(worker,(5,))
threads.append(t1)
threads.append(t2)
for t in threads:
t.start()
for t in threads:
t.join()
print('【主函数执行结束于:{}】'.format(time.ctime()))
if __name__ == '__main__':
main()
3).同步原语(锁):.Lock类
.acquire()----获得
.release()----释放
支持上下文操作:with lock:
import time
import threading
import random
eggs = []
lock = threading.Lock()
def put_eggs(n,lst):
lock.acquire()
for i in range(1,n+1):
time.sleep(random.randint(0,1))
lst.append(i)
lock.release()
#另一种写法:
# with lock:
# for i in range(1,n+1):
# time.sleep(random.randint(0,3))
# lst.append(i)
def main():
threads = []
for i in range(3):
t = threading.Thread(target=put_eggs,args=(4,eggs))
threads.append(t)
for t in threads:
t.start()
for t in threads:
t.join()
print(eggs)
if __name__ == '__main__':
main()
4.queue(队列)模块,模块内有三种不同的类:
queue.Queue----FIFO,先入先出队列
queue.LifoQueue----LIFO,后入先出队列
queue.PriorityQueue----可自定顺序,优先队列
对于队列的类,可调用以下函数:
.put(item,block=True,timeout=None)----放入数据项,block为阻塞,timeout为等待延时
.get(block=True,timeout=None)----获取数据项
.task_done()----声明当前队列任务处理完毕
.join()----队列所有项处理完毕前阻塞
例:创建一个FIFO的queue,单线程放数,双线程取数。
import threading
import time
import random
import queue
def data_in(data_queue,n):
for i in range(1,n+1):
time.sleep(0.5)
item = random.randint(1,100)
data_queue.put(item)
print('{}线程放入数据:{}'.format(threading.current_thread().name,item))
def data_out(data_queue):
while True:
try:
item = data_queue.get(timeout=2)
print('{}线程拿出数据:{}'.format(threading.current_thread().name,item))
except queue.Empty:
break
else:
data_queue.task_done()
def main():
threads = []
q = queue.Queue()
p = threading.Thread(target=data_in,args=(q,4))
p.start()
for i in range(2):
t = threading.Thread(target=data_out,args=(q,))
threads.append(t)
for t in threads:
t.start()
for t in threads:
t.join()
q.join()
if __name__ == '__main__':
main()
注:Queue.task_done()函数向任务已经完成的队列发送一个信号,Queue.join() 实际上意味着等到队列为空,再执行别的操作。如果线程里每从队列里取一次,但没有执行task_done(),则join无法判断队列到底有没有结束,在最后执行个join()是等不到结果的,会一直挂起。
5.multiprocessing多进程模块:充分利用多核、多CPU的计算能力,适用于计算密集型任务
import time
import multiprocessing
def func(n):
print('{}执行开始于:{}'.format(multiprocessing.current_process().name,time.ctime()))
time.sleep(n)
print('{}执行结束于:{}'.format(multiprocessing.current_process().name, time.ctime()))
def main():
print('{}主函数执行开始于:{}'.format(multiprocessing.current_process().name,time.ctime()))
processes = []
p1 = multiprocessing.Process(target=func,args=(4,))
processes.append(p1)
p2 = multiprocessing.Process(target=func, args=(2,))
processes.append(p2)
for p in processes:
p.start()
for p in processes:
p.join()
print('{}主函数执行结束于:{}'.format(multiprocessing.current_process().name, time.ctime()))
if __name__ == '__main__':
main()
6.快速实现多进程多线程的封装模块:concurrent.futures模块
多线程:concurrent.futures.ThreadPoolExecutor(max_workers=最大线程数)----不是类,是执行器,只需调用其中的.submit(函数,参数(不需要以元组的形式))函数。
多进程(类似):concurrent.futures.ProcessPoolExecutor(max_workers=最大线程数)----只需调用其中的.submit(函数,参数(不需要以元组的形式))函数。
二.装饰器
1.概述:用于管理和增强函数和类行为的代码,提供一种在函数或类定义中插入自动运行代码的机制。
注:函数可以赋给变量,可以作为参数传递,也可以嵌套。
2.函数定义装饰器:即用函数来定义装饰器
def p_decorator(func):
#定义一个嵌套的封装函数,描述修饰后的函数
#(*args,**kwargs)为参数通配符,表示任意类型任意个数的参数
def wrapper(*args,**kwargs):
return '<p>' + func(*args,**kwargs) + '</p>'
return wrapper
@p_decorator
def text_upper(text):
return text.upper()
if __name__ == '__main__':
print(text_upper('abcde'))
注:在要装饰的函数前,加"@函数名"即为用该函数装饰,如@p_decorator则是用p_decorator函数装饰text_upper函数,装饰后,text_upper =p_decorator(text_upper)。
3.类定义装饰器:
class P:
def __init__(self,func):
self.func = func
# 类像函数一样被调用时,就相当于执行它的__ call__方法.
def __call__(self,*args,**kwargs):
return '<p>' + self.func(*args,**kwargs) + '<p/>'
@P
def text_upper(text):
return text.upper()
注:在要装饰的函数前加@P,等价于text_upper=P(text_upper)。
函数定义的装饰器,既可以用在普通函数上,也可以用在类的方法上;而类定义的装饰器,在用在某个类的方法中时,编译无法分清参数self指的是什么。因此如果要装饰类中的方法,推荐函数定义的装饰器。
4.参数化装饰器:再多一层嵌套,返回的是用tag装饰器,而tag装饰器返回的是封装好的函数。
def tags(tag):
def tag_decorator(func):
def wrapper(*args,**kwargs):
return '<{}>{}</{}>'.format(tag,func(*args,**kwargs),tag)
return wrapper
return tag_decorator
@tags('p')
def text_upper(text):
return text.upper()
备注:
1.mt----Multi Thread多线程
2.实际编程中,首先要确定任务类型,如果是计算密集型的,则多进程更好一些,如果是IO密集型,需要随时释放GIL的,则多线程更好一些。
3.(*args,**kwargs)为参数通配符,表示任意类型任意个数的参数。