机器学习-python语言基础第十二天

并发编程

概念

非并发编程是程序由单步骤序列构成,包含独立子任务的程序运行效率低。
并发编程是异步、高效的,它能分解子任务,简化流程与逻辑。

  • 进程process:一个程序的执行实例,每个进程都有自己的地址空间、内存、数据栈及辅助数据
  • 线程thread: 同一进程内,可被并行激活的控制流,共享相同上下文(空间地址、数据结构),便于信息共享和通信,线程访问顺序差异会导致结果不一致
  • 全局解释器锁(GIL:Global Interpreter Lock):Python 代码由虚拟机(解释器主循环)控制,主循环同时只能有一个控制线程执行,全局解释器锁用来控制线程占用主程序的开关,保证主程序一直有线程使用。
多线程

_thread模块:没有控制进程结束机制,只有一个同步原语锁,功能要少于threading模块,但可以作为线程入门的模块,前面加了一个横线表示模块已经不被使用了。
内部的方法.start_new_thread(funciton,args,**kwargs=None) 开始线程:

def worker(n):
    print('函数执行开始于: {}'.format(time.ctime()))
    time.sleep(n)
    print(f'函数结束于:{time.ctime()}')


def main():
    print(f'【主函数执行开始于:{time.ctime()}】')
    _thread.start_new_thread(worker, (2,))
    _thread.start_new_thread(worker, (4,))
    time.sleep(5)
    print(f'【主函数结束于:{time.ctime()}】')

上述之所以不是相加的时间,是因为每个函数在占用主程序时,并没有添加锁机制,且函数运行内容少,不影响效率,因此在抢占式的多线程机制下,看起来像同时运行。
threading模块:构造线程方法有两种

  • .Thread(target=目标函数,args=(参数,)).start() 启动线程 .join() 要求主线程等待 .name 线程名称
def worker(n):
    print('{}函数执行开始于: {}'.format(threading.current_thread().name,time.ctime()))
    time.sleep(n)
    print(f'{threading.current_thread().name}函数结束于:{time.ctime()}')


def main():
    print(f'【主函数执行开始于:{time.ctime()}】')
    thread = []
    t1 = threading.Thread(target=worker,args=(4,))
    thread.append(t1)
    t2 = threading.Thread(target=worker, args=(2,))
    thread.append(t2)

    for t in thread:
        t.start()

    for t in thread:  #不能跟上述代码写在一起
        t.join()


    print(f'【主函数结束于:{time.ctime()}】')
  • 自定义Thread派生类,重写方法逻辑。
def worker(n):
    print('{}函数执行开始于: {}'.format(threading.current_thread().name, time.ctime()))
    time.sleep(n)
    print(f'{threading.current_thread().name}函数结束于:{time.ctime()}')


class MyThread(threading.Thread):
    def __init__(self, func, args):
        threading.Thread.__init__(self)
        self.func = func
        self.args = args

    def run(self):
        self.func(*self.args)


def main():
    print(f'【主函数执行开始于:{time.ctime()}】')
    thread = []
    t1 = MyThread(worker, (4,))
    thread.append(t1)
    t2 = MyThread(worker, (2,))
    thread.append(t2)

    for t in thread:
        t.start()

    for t in thread:  # 不能跟上述代码写在一起
        t.join()
    print(f'【主函数结束于:{time.ctime()}】')

.current_thread() 获取当前线程

  • threading.Lock同步原语:锁。.acquire() 获得 .release() 释放。支持上下文操作 with lock:
egg = []
lock = threading.Lock()


def put_egg(n, lst):
    lock.acquire()
    for i in range(1, n + 1):
        time.sleep(random.randint(0, 2))
        lst.append(i)
    lock.release()


def main():
    threads = []

    for i in range(3):
        t = threading.Thread(target=put_egg, args=(5,egg))
        threads.append(t)

    for t in threads:
        t.start()

    for t in threads:
        t.join()


    print(egg)

使用上下文同步机制的with lock,可以省去释放锁的操作。

队列

队列可以作为多进程或线程数据共享的一种机制,也可以实现进程间的同步。
队列主要包括:先进先出队列FIFO、先进后出队列LIFO、优先级队列Priority Queue。其中FIFO队列在python模块中主要时queue模块。

  • .Queue(maxsize=0) 构造实力
  • .put(item,block=True,timeout=None) 放入数据项
  • .get(block=True , timeout=None) 获取数据项,自己不会对计数器减1
  • .task_done() 声明当前队列任务处理完毕,会将队列计数减1
  • .join() 队列所有项处理完毕前阻塞,一般是队列内的基数单元为0时
def producer(data_queue):
    for i in range(100):
        time.sleep(0.5)
        item = random.randint(1, 100)
        data_queue.put(item)
        print(f'{threading.current_thread().name}在队列中放入数据项:{item}')


def consumer(data_queue):
    while True:
        try:
            item = data_queue.get(timeout=3)  #不设置的话会阻塞在这里等待,当超时时,会返回queue.Empty的错误
            print(f'{threading.current_thread().name}在队列中移除数据项:{item}')
        except queue.Empty:
            break
        else:
            data_queue.task_done()


def main():
    q = queue.Queue()

    threads = []
    p = threading.Thread(target=producer, args=(q,))
    p.start()

    for i in range(2):
        c = threading.Thread(target=consumer, args=(q,))
        threads.append(c)

    for t in threads:
        t.start()

    for t in threads:
        t.join()

    q.join()

因为i时间设置的刚好,才会出现两个线程交叉运行,加入填入数据的时间稍微长点,很可能一个线程独占取数据,当超过线程的等待时间时,线程就运行结束,只能存入数据,无法取数据。

multiprocessing 模块

充分运用多核、多cpu的计算能力,适用于计算机密集型任务,python的解释器会自动分配多核运行。用法同多线程一样。

def func(n):
    print(f'{multiprocessing.current_process().name}执行开始:{time.ctime()}')
    time.sleep(n)
    print(f'{multiprocessing.current_process().name}执行开始:{time.ctime()}')


def main():
    print(f'主函数开始运行:{time.ctime()}')

    process = list()

    p1 = multiprocessing.Process(target=func, args=(4,))
    process.append(p1)
    p2 = multiprocessing.Process(target=func, args=(2,))
    process.append(p2)

    for p in process:
        p.start()

    for p in process:
        p.join()

    print(f'主函数结束运行:{time.ctime()}')
线程/进程池

线程/进程池主要模块是:concurrent.futures 模块,包括如下主要部分

  • ThreadPoolExecutor
  • ProcessPoolExcecutor
numbers = list(range(1, 11))


def count(n):
    for i in range(100000000):
        i += i
    return i * n


def worker(x):
    result = count(x)
    print(f'数字:{x}的结果是:{result}')


# 执行顺序
def sequential_execution():
    start_time = time.clock()
    for i in numbers:
        worker(i)
    print(f'顺序执行时间:{time.clock() - start_time}')


# 线程池执行

def threading_execution():
    start_time = time.clock()
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        for i in numbers:
            executor.submit(worker, i)

    print(f'线程池花费时间:{time.clock() - start_time}')


# 进程池执行

def process_execution():
    start_time = time.clock()
    with concurrent.futures.ProcessPoolExecutor(max_workers=5) as executor:
        for i in numbers:
            executor.submit(worker, i)

    print(f'线程池花费时间:{time.clock() - start_time}')

用法和逻辑与前速稍不同,这个池主要是把要运行的函数交给它,它自动分配这些池。这里线程池的运行时间与直接运行的时间要长,是因为要做线程之间的切换。

装饰器

概述

用于管理和增强函数和类行为的代码,提供一种在函数或类定义中插入自动运行代码的机制,
特点

  • 更明确的语法
  • 更高的代码可维护性
  • 更好的一致性

先来回顾一下函数变量的LEGB:local、enclosure、global、built-in

函数定义装饰器
def p_decorator(func):
    def wrapper(*args, **kwargs):
        return '<p>' + func(*args, **kwargs) + '<p>'

    return wrapper
def div_decorator(func):
    def wrapper(*args, **kwargs):
        return '<div>' + func(*args, **kwargs) + '<div>'

    return wrapper
@div_decorator
@p_decorator
def get_text():
    return '欢迎'
    
class Student:
    def __init__(self,name):
        self.name = name


    @p_decorator
    def get_name(self):
        return self.name.upper()

if __name__ == '__main__':
    # html = p_decorator(get_text)  等效代码
    # print(html())
    print(get_text())  


    s =Student('mike')
    print(s.get_name())
类定义装饰器
class P:
    def __init__(self,func):
        self.func = func

    def __call__(self, *args, **kwargs):
        return '<p>'+self.func(*args,**kwargs)+'<p>'
@P
def get_text():
    return '欢迎'
  
if __name__ == '__main__':
    # html = P(get_text)
    # print(html())
    print(get_text())
装饰器参数
def tags(tag):
    def tag_decorator(func):
        def wrapper(*args, **kwargs):
            return f'<{tag}>{func(*args, **kwargs)}<{tag}>'

        return wrapper
    return tag_decorator   #返回函数本身

@tags('d')
def get_upper_text(text):
    return text.upper()

if __name__ == '__main__':
   print(get_upper_text('www'))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值