• 25 - 线程池和指令系统


    Day 25

    一、线程等待(阻塞)

    1.子线程对象.join

    • 可以阻塞当前线程进城,使其在完成指定线程之后在继续执行该线程
    • 示例:我想利用多线程下载三部电影,在电影全部下载完成之后在打印”全部下载完成“。
    from threading import Thread
    from time import sleep
    from datetime import datetime
    from random import randint
    
    
    
    def download(name):
    	print(f'{name}开始下载:{datetime()}')
    	sleep(randint(2, 7))
    	print(f'{name}下载完成:{datetime()}')
    
    
    if __name__ == '__main__':
    
        t1 = Thread(target=download, args=('明日战纪',))
        t2 = Thread(target=download, args=('斗罗大陆',))
        t3 = Thread(target=download, args=('独行月球',))
    # 示例1一:
        t1.start()
        t2.start()
        t3.start()
    
        # 1. 子线程对象.join()   -   阻塞当前线程直到指定子线程任务完成
        t1.join()
        t2.join()
        t3.join()
        print('==============全部下载完成!============')
        
    # 示例2:前两个电影都下载完成后才下载第三个电影
        t1.start()
        t2.start()
    
        t1.join()
        t2.join()
    
        t3.start()
        t3.join()
        print('==============全部下载完成!============')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    二、线程池

    1.线程池工作原理

    • 线程池就是管理多个线程的工具
    • 先创建指定个数的线程,最后添加多个任务(任务数量>线程数量)让线程池中的线程去执行添加所有的任务,直到所有任务执行完成(线程池中的每个线程可能会多执行多个任务)
    • 在使用线程池的时候需要先导入相关模块
    from threading import Thread, current_thread
    from concurrent.futures import ThreadPoolExecutor
    
    • 1
    • 2
    • 示例:如我想下载1000部电影,我不会一个一个去创建子线程,遍可以调用线程池

    2.线程池添加任务

    • 一次添加一个任务
    • 注意:实参的数量由前面的函数在调用的时候需要的实参来决定
      • 线程池.submit(函数, 实参1, 实参2, 实参3,…)
    • 同时添加多个任务
    • 注意:使用map添加多个任务的时候,任务对应的函数必须是有且只有一个参数的函数
      • 线程池.submit(函数, 实参1, 实参2, 实参3,…)
    # 导入相关模块
    from threading import Thread, current_thread
    from time import sleep
    from datetime import datetime
    from random import randint
    from concurrent.futures import ThreadPoolExecutor
    
    f'{name}开始下载:{datetime.now()}', current_thread()
    # 创建函数模拟下载电影
    def download(name):
    	print(f'{name}开始下载:{datetime.now()}', current_thread())
    	sleep(randint(2, 7))
        print(f'{name}下载结束:{datetime.now()}')
    
    
    # 开始下载电影
    # 方案1:直接使用多线程下载1000个电影
    if __name__ == '__main__':
        num = 0
        for _ in range(11):
     		# 创建空列表保存每一个子线程
     		ts = []
            for x in range(101):
                num += 1
                t = Thread(target=download, args=(f'电影{num}',))
                ts.append(t)
                t.start()
            # 循环调取每一个添加的子线程,直到全部运行结束,打印下载完成
            for x in ts:
                x.join()
            print('全部下载完成')
    
    
    
    # 方案2:使用线程池下载1000个电影
    if __name__ == '__main__':
    	pool = ThreadPoolExecutor(3)
    	# 一次添加一个任务: submit
    	pool.submit(download, '肖生克的救赎')
        pool.submit(download, '霸王别姬')
        # 一次添加多个任务
        pool.map(download, ['V字仇杀队', '恐怖游轮', '沉默的羔羊'])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    3.关闭线程池

    • 线程池关闭后无法再添加新的任务,并且会阻塞当前线程等待整个线程池的任务都完成
    # 下载任务完成后,可以关闭线程池,等所有任务完成后打印结果
    pool.shutdown()
        print('==============完成!=============')
    
    • 1
    • 2
    • 3

    练习:利用爬虫所学,爬取某网站数据,要求,使用线程池提高效率。

    # 导入相关模块
    import requests
    from bs4 import BeautifulSoup
    import csv
    from concurrent.futures import ThreadPoolExecutor
    
    # 创建访问函数
    def get_nte_date(url:str):
        headers = {
            'user-agent':'Mozilla/4.0 (Windows NT 10.0; Win32; x64) AppleWebKit/522.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
        }
        response = requests.get(url, headers=headers)
        result = response.text
        # 直接调用解析函数
        analysis_data(result)
    
    # 解析数据
    def analysis_data(html:str):
        # 每一页数据以列表形式保存
        all_data = []
        # BeautifulSoup解析数据
        soup = BeautifulSoup(html,'lxml')
        all_div = soup.select('.grid_view>li>div.item')
        for div in all_div:
            rank = div.select_one('div.pic>em').text
            name = div.select_one('div.hd>a>span.title').text
            link = div.select_one('div.pic>a').attrs['href']
            info = div.select_one('.bd>p').text.strip().split('\n')[-1].strip()
            info_list = info.split('/')
            time = info_list[0]
            country = info_list[-2]
            category = info_list[-1]
            score = div.select_one('.rating_num').text
            comment_count = div.select('.star>span')[-1].text[:-3]
            intro_span = div.select_one('.inq')
            if intro_span:
                intro = intro_span.text
            else:
                intro = ''
            all_data.append([int(rank), name, link, score, time.strip(), country.strip(), category.strip(), comment_count, intro])
            # 将没页数据添加到总表
            films.append(all_data)
    
    
    if __name__ == '__main__':
        # 创建总表保存每页数据
        films = []
        # 创建csv文件实现数据持久化
        f = open('file/豆瓣数据.csv', 'w', encoding='utf-8')
        writer = csv.writer(f)
        writer.writerow(['排名', '电影名称', '链接', '评分', '上映时间', '国家', '类型', '评论数', '简介'])
        # 使用线程池提高效率
        pool = ThreadPoolExecutor(10)
        for i in range(0, 251, 25):
            url = f'https://movie.douban.com/top250?start={i}&filter='
            pool.submit(get_nte_date, url)
    
        pool.shutdown()
        # 排序  利用列表比较原则
        films.sort()
        # 将数据写入文件
        for x in films:
            writer.writerows(x)
        # 保存文件
        f.close()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    三、常用指令

    执行指令的工具: Windows - 命令提示符(cmd) 、Mac - 终端

    1. 运行python程序: - 运算程序的计算机必须先安装python环境

    • win: python py文件路径
    • mac: python3 py文件路径

    2. 进入文件夹: cd

    • cd 文件夹相对路径、文件夹绝对路径
    • 注意:如果是windows操作系统,cd操作如果要跨盘需要先切盘,然后再cd
    • 切盘方法:C:、E:、D

    3. 查看当前文件夹的内容

    • win: dir
    • Mac:ls

    4. 用指令创建虚拟环境

    • 第一步:找到一个用来放虚拟环境的文件夹

    • 第二步:通过cd指令进入到存放虚拟环境的文件夹中

    • 第三步:创建虚拟环境

      • win: python -m venv 虚拟环境名
      • mac:python3 -m venv 虚拟环境名
    • 第四步:激活虚拟环境

      • (mac) source 虚拟环境目录/bin/activate
      • (windows) 虚拟环境目录\ Scripts\activate.bat
    • 第五步:退出虚拟环境 – deactivate

    5.常用pip指令(pip - Python包管理工具)

    • pip list - 查看当前环境已经安装过的所有的第三方库
    • pip install 第三方库名称 - 下载并且安装指定的第三方库
    • pip install 第三方库名称 -i 镜像地址 - 在指定的镜像地址中下载安装
    • pip install 第三方库名称==版本号 -i 镜像地址
    • pip install 第三方库名称1 第三方库名称2
    • pip freeze > 依赖文件名 - 生成依赖文件
    • pip install -r 依赖文件路径 - 批量安装
    • pip uninstall 第三方库名称 - 卸载指定的第三方库
  • 相关阅读:
    【文章阅读】Frustratingly Simple Few-Shot Object Detection
    【国产MCU】-CH32V307-通用定时器(GPTM)-编码模式与旋转编码器驱动
    假期AI新闻热点:亚运会Al技术亮点;微软GPT-4V论文精读;Perplexity推出pplx-api;DALL-E 3多渠道测评 | ShowMeAI日报
    干货分享|优炫数据库支撑GIS融合的探索
    【ijkplayer】解码流程梳理(三)
    Mybatis缓存
    强光LCD液晶显示屏检测装置太阳光模拟器系统
    Java 创建线程的三种方式
    《算法系列》之并查集
    【消息中间件】RocketMQ设计浅析
  • 原文地址:https://blog.csdn.net/Mr_suyi/article/details/126437479