python:并发编程、装饰器

第一部分: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)为参数通配符,表示任意类型任意个数的参数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值