day27-迭代器协议,协程,同步异步

day27

总结

  • review

    """
    !./env python
    -*- coding: utf-8 -*-
    @Time:  2021/6/3 9:21
    @Author:    三玖天下第一
    @File: review.py
    @Software: PyCharm
    
    回顾知识点
    """
    
    """
    创建线程的三种方式:
        1. 创建Thread对象 ---> target / args ---> start()
        2. 继承Thread类 ---> 重写run()方法 ---> start()
        3. 使用线程池(最佳选择)---> 减少频繁创建和释放线程造成的系统开销
    
    线程间的通信非常简单,可以通过共享内存来实现
    进程间的通信比较麻烦,因为进程间的内存是相互隔离的,需要使用管道、套接字等方式来通信
    
    多进程 ---> 计算密集型任务 ---> 科学计算 / 音视频编解码 / 加密算法
    
    多线程
    异步I/O ---> I/O密集型任务 ---> Web应用 / 爬虫 / 数据读写
    
    什么时候会使用多线程???
        ~ 任务耗费的时间比较长(会造成其他任务的阻塞)
        ~ 任务之间没有偏序关系(一个任务不用等另一个任务执行完毕)
    
    多线程程序的好处?
        ~ 提升执行效率
        ~ 改善用户体验
    """
    
    
    """
    I/O操作模式:
        同步:调用一个函数,必须等函数返回以后代码才能往下执行
            -   阻塞:
            -   非阻塞:
        异步:调用一个函数,不必等函数返回代码就可以往下执行
            -   阻塞: ---> 多路I/O复用
            -   非阻塞:    ---> 回调式I/O
    
    jAVA ---> bto   ----> NTO   ---> AIO
    
    Python做并发(并行)编程的三种方式:
        ~ 多线程
        ~ 多进程
        ~ 异步编程/异步IO ---> 协作式
        
    迭代器:实现了迭代器协议的对象
    迭代器协议对应两个魔术方法:__iter__、__next__
        ~ __iter__: 获取迭代器对象
        ~ __next__: 获取到下一个值
        
        
    妈妈让我去厨房烧一锅水,准备下饺子
    阻塞:水只要没烧开,我就干瞪眼看着这个锅,沧海桑田,日新月异,我自岿然不动,厨房就是我的家,烧水是我的宿命。
    
    非阻塞:我先去我屋子里打把王者,但是每过一分钟,我都要去厨房瞅一眼,生怕时间长了,水烧干了就坏了,这样导致我游戏也心思打,果不然,又掉段了。
    
    同步:不管是每分钟过来看一眼锅,还是寸步不离的一直看着锅,只要我不去看,我就不知道水烧好没有,浪费时间啊,一寸光阴一寸金,这锅必须发我13薪
    
    异步:我在淘宝买了一个电水壶,只要水开了,它就发出响声,嗨呀,可以安心打王者喽,打完可以吃饺子喽~
    
    总结:
    阻塞/非阻塞:我在等你干活的时候我在干啥?
    阻塞:啥也不干,死等
    非阻塞:可以干别的,但也要时不时问问你的进度
    同步/异步:你干完了,怎么让我知道呢?
    同步:我只要不问,你就不告诉我
    异步:你干完了,直接喊我过来就行
    
    """
    
    """
    作业:写一个可以迭代出从2开始的若干个质数(只能被1和自身整除的正整数)
    """
    
    from threading import Thread
    from multiprocessing import Process, freeze_support
    
    
    def run_task():
        while True:
            pass
    
    
    if __name__ == '__main__':
        freeze_support()
        for _ in range(5):
            p = Process(target=run_task)
            p.start()
            p.terminate()  # 结束进程
    
        for _ in range(5):
            t = Thread(target=run_task)
            t.start()
    
  • 守护线程

    """
    !./env python
    -*- coding: utf-8 -*-
    @Time:  2021/6/3 10:00
    @Author:    三玖天下第一
    @File: example02.py
    @Software: PyCharm
    
    守护线程    -   不值得
    主线程结束,子线程结束
    """
    import time
    from concurrent.futures import ThreadPoolExecutor
    from threading import Thread
    
    
    def output(content):
        while True:
            print(content, end='')
            time.sleep(0.01)
    
    
    if __name__ == '__main__':
        Thread(target=output, args=('Ping',), daemon=True).start()
        Thread(target=output, args=('Pong',), daemon=True).start()
        # 子线程中有一个没停主线程就没停,所以只要有一个不是守护线程,所有的子线程都不会停
        # Thread(target=output, args=('Pong',), daemon=False).start()
        # thread_pool = ThreadPoolExecutor(max_workers=20)
        # for _ in range(10):
        #     thread_pool.submit(output, 'Pong')
        # thread_pool.shutdown(wait=True)
    
    
  • """
    !./env python
    -*- coding: utf-8 -*-
    @Time:  2021/6/3 10:11
    @Author:    三玖天下第一
    @File: example03.py
    @Software: PyCharm
    
    定义一个类描述一个银行账户(余额为0,存钱,取钱)
    创建一个银行账户对象,启动100个线程,每个线程向该用户转1元
    转账完成后查看银行账户余额。注意:存钱和取钱的受理需要耗费时间
    
    当多个线程竞争一个资源的时候,如果资源本身并不是线程安全的,会出现问题
    如果代码中需要对数据进行保护(保护临界资源),需要对数据加锁
    
    如果对象实现了上下文管理协议,就可以用到with语法中
    上下文管理器就是两个魔术方法:
        - __enter__(self):  加锁
        _ __exit__(self):   释放锁
    """
    import time
    from threading import Thread, RLock
    
    
    class BankAccount:
    
        def __init__(self, name):
            self.name = name
            self.id = '139485682642873'
            self.balance = 1000
            # 重入锁(一般情况下我们使用的锁都是重入锁)
            self.lock = RLock()
    
        def save_money(self, money):
            """
            存钱
            # 如果多个现场同时执行这行代码,那么后面的线程对余额的更新会覆盖前面的更新
            # 这个现象在数据库层面有可能发生,我们叫丢失更新(第一类丢失更新和第二类丢失更新)
            :param money: 需要存钱金额
            :return:    None
            """
            # 获得锁
            with self.lock:
                new_balance = money + self.balance
                time.sleep(0.01)
                self.balance = new_balance
    
        def get_balance(self):
            return self.balance
    
        def draw_money(self, money):
            """取钱"""
            if money <= self.balance:
                with self.lock:
                    new_balance = self.balance - money
                    time.sleep(0.02)
                    self.balance = new_balance
                    return money
            return 0
    
    
    if __name__ == '__main__':
        account = BankAccount('小玖')
        thread_list = []
        for _ in range(100):
            t = Thread(target=account.save_money, args=(1, ))
            t.start()
            thread_list.append(t)
    
        # for _ in range(60):
        #     t = Thread(target=account.draw_money, args=(1,))
        #     t.start()
        #     thread_list.append(t)
    
        for thread in thread_list:
            thread.join()
        print(f'余额:{account.get_balance()}')
    
    
    
    
  • 线程池

    """
    !./env python
    -*- coding: utf-8 -*-
    @Time:  2021/6/3 11:26
    @Author:    三玖天下第一
    @File: example04.py
    @Software: PyCharm
    
    创建和释放一个线程都会产生比较大的开销,所以程序要长时间的使用多个线程
    最好的方式是通过线程池来使用线程
    基本上不需要创建和释放线程,程序和线程池之间是借还关系
    
    池化技术是一种典型的空间换时间的技术
    """
    
    import time
    from concurrent.futures import ThreadPoolExecutor
    from multiprocessing.queues import Queue
    from queue import Queue
    from threading import RLock
    
    
    class BankAccount:
    
        def __init__(self, name):
            self.name = name
            self.id = '139485682642873'
            self.balance = 1000
            # 重入锁(一般情况下我们使用的锁都是重入锁)
            self.lock = RLock()
    
        def save_money(self, money):
            """
            存钱
            # 如果多个现场同时执行这行代码,那么后面的线程对余额的更新会覆盖前面的更新
            # 这个现象在数据库层面有可能发生,我们叫丢失更新(第一类丢失更新和第二类丢失更新)
            :param money: 需要存钱金额
            :return:    None
            """
            # 获得锁
            with self.lock:
                new_balance = money + self.balance
                time.sleep(0.01)
                self.balance = new_balance
    
        def get_balance(self):
            return self.balance
    
        def draw_money(self, money):
            """取钱"""
            if money <= self.balance:
                with self.lock:
                    new_balance = self.balance - money
                    time.sleep(0.02)
                    self.balance = new_balance
                    return money
            return 0
    
    
    if __name__ == '__main__':
        account = BankAccount('小玖')
        print(account.balance)
        # pool = ThreadPoolExecutor(max_workers=64)
        # for _ in range(100):
        #     res = pool.submit(account.save_money, 1)
        # pool.shutdown()
        with ThreadPoolExecutor(max_workers=256) as pool:
            for _ in range(1000):
                pool.submit(account.save_money, 1)
        print(account.balance)
    
    
    
  • 迭代器协议

    """
    !./env python
    -*- coding: utf-8 -*-
    @Time:  2021/6/3 14:46
    @Author:    三玖天下第一
    @File: example06.py
    @Software: PyCharm
    """
    import requests
    import time
    
    class FibIter:
        def __init__(self, num):
            self.a, self.b = 0, 1
            self.num = num
            self.counter = 0
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.counter < self.num:
                self.a, self.b = self.b, self.a+self.b
                self.counter += 1
                return self.a
            raise StopIteration()
    
    
    if __name__ == '__main__':
        fiter = FibIter(20000)
        for x in fiter:
            print(x)
    
  • 协程

    """
    !./env python
    -*- coding: utf-8 -*-
    @Time:  2021/6/3 15:08
    @Author:    三玖天下第一
    @File: example08.py
    @Software: PyCharm
    """
    import time
    
    """
    协程(coroutine): 互相协作的子程序(两个函数)
    生成器经过预激活操作就可以升级为一个协程
    
    yield   --->    产出 / 让步
    """
    
    def func():
        sum1 = 1
        for x in range(10):
            sum1 *= x
            time.sleep(0.1)
        return sum1
    
    def calc_avg():
        total, counter = 0, 0
        avg_value = None
        while True:
            curr_num = yield avg_value
            total += curr_num
            counter += 1
            avg_value = total / counter
    
    
    def main():
        gen = calc_avg()
        print(type(gen))  # <class 'generator'>
        # 将生成器预激活 ---> 携程
        # next(gen)
        print(gen.send(None))
    
        for _ in range(5):
            num = int(input())
            print(gen.send(num))
    
    
    if __name__ == '__main__':
        main()
    
    
  • 异步

    """
    !./env python
    -*- coding: utf-8 -*-
    @Time:  2021/6/3 16:09
    @Author:    三玖天下第一
    @File: example9.py
    @Software: PyCharm
    
    
    使用异步函数来创建协程对象
    async   ---> asynchronous
    sync    ---> synchronous
    """
    
    # 调用该函数不是执行函数体而是得到一个协程对象
    # 协程对象需要挂载到一个事件循环上才能运转起来
    import asyncio
    
    
    async def say_hello():
        print('hello, world')
    
    
    if __name__ == '__main__':
        loop = asyncio.get_event_loop()
        loop.run_until_complete(say_hello())
        loop.close()
    
    
  • """
    !./env python
    -*- coding: utf-8 -*-
    @Time:  2021/6/3 16:20
    @Author:    三玖天下第一
    @File: example10.py
    @Software: PyCharm
    
    同步  ---> 有序, 异步 ---> 无序
    异步非阻塞式调用    ---> 写作式并发  ---> 多个子线程相互协作提高效率
    """
    import time
    import asyncio
    
    
    def display(num):
        print(num)
        time.sleep(1)
    
    
    async def asy_display(num):
        print(num)
        hhe = await asyncio.sleep(1)
        print(hhe)
        await asyncio.sleep(3)
        # asyncio.sleep(1)
    
    
    def main():
        for num in range(9):
            display(num)
    
    
    def main2():
        cos = [asy_display(num) for num in range(9)]
        loop = asyncio.get_event_loop()
        # asyncio.wait(cos) ===>
        # await cos[0]
        # await cos[1]
        # await cos[2]
        # ...
        loop.run_until_complete(asyncio.wait(cos))
    
        # cos = asy_display(10)
        # loop = asyncio.get_event_loop()
        # loop.run_until_complete(cos)
    
    async def main3():
        await asy_display(10)
    
    if __name__ == '__main__':
        # main()
        # main2()
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main3())
        loop.close()
    
    

作业

  • 质数迭代器

    class PrimeNumber:
        def __init__(self, num):
            self.digit = 2
            self.counter = 0
            self.num = num
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.counter < self.num:
                if self.counter == 0 and self.digit == 2:
                    self.counter += 1
                    return self.digit
                while True:
                    self.digit += 1
                    for x in range(2, int(self.digit**0.5)+1):
                        if self.digit % x == 0:
                            break
                    else:
                        self.counter += 1
                        return self.digit
            raise StopIteration()
    
    
    if __name__ == '__main__':
        prime_num = PrimeNumber(10000)
        for x in prime_num:
            print(x)
    
  • aiohttp爬取网页

    import aiohttp
    import asyncio
    from bs4 import BeautifulSoup
    
    
    async def analysis(html):
        soup = BeautifulSoup(html, 'lxml')
        return soup.select_one('head > title').get_text()
    
    
    async def main(url, analysis, save_func):
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3861.400 QQBrowser/10.7.4313.400"}
        html = await get_html(url, headers=headers)
        content = await analysis(html)
        await save_func(content)
    
    
    async def get_html(url, *, headers={}, params={}, proxy=''):
        async with aiohttp.ClientSession() as session:
            async with session.get(url, headers=headers, params=params, proxy=proxy) as response:
                return await response.text()
    
    
    async def post_html(url, *, headers={}, data={}, proxy=''):
        async with aiohttp.ClientSession() as session:
            async with session.post(url, headers=headers, data=data, proxy=proxy) as response:
                return await response.text()
    
    
    async def save(data):
        print(data)
    
    
    if __name__ == '__main__':
        loop = asyncio.get_event_loop()
        urls = ['http://python.org', 'https://www.baidu.com', 'https://www.jd.com',
                'https://now.qq.com/pcweb/topic.html', 'https://mini.yyrtv.com',
                'https://www.runoob.com/python3/python3-tutorial.html', 'https://www.52pojie.cn/thread-389591-1-1.html',
                'https://ext.dcloud.net.cn/plugin?id=148', 'http://www.rjkflm.com/andtest', 'https://www.iconfont.cn/']
        cos = [main(url, analysis, save) for url in urls]
        loop.run_until_complete(asyncio.wait(cos))
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值