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))