第八天 线程池和进程池

from queue import Queue as T_Queue # 线程队列
from multiprocessing import Process, Queue as P_Queue # 进程队列

1. 线程间通信

同一个进程中的多个线程,数据可以直接共享
数据收集:定义全局容器,常用的容器 - 线程队列 (定义全局队列直接使用)
添加数据: 队列.put(数据)
获取数据: 队列.get() 、 队列.get(timeout=时间)

2. 进程间通信

只能通过进程队列来进行数据交流
在一个进程中定义的进程队列,如果需要在其他进程中使用,必须通过函数的参数传递到其他进程中
添加数据: 队列.put(数据)
获取数据: 队列.get() 、 队列.get(timeout=时间)

线程池的作用

import time
from datetime import datetime
from random import randint
from threading import Thread, current_thread
from concurrent.futures import ThreadPoolExecutor


def download(name):
    print(f'{name}开始下载:{datetime.now()}')
    print(current_thread())
    time.sleep(randint(2, 6))
    print(f'{name}下载结束:{datetime.now()}')


if __name__ == '__main__':
    names = [f'电影{x}' for x in range(1, 11)]

    # 方案一:
    # for x in range(2):
    #     ts = []
    #     for y in range(5):
    #         t = Thread(target=download, args=(names[x*5 + y],))
    #         t.start()
    #         ts.append(t)
    #     for t in ts:
    #         t.join()
    #     print(f'=======================第{x+1}批完成===========================')

    # 方案二
    pool = ThreadPoolExecutor(max_workers=5)
    pool.map(download, names)

1. 线程池

线程池:装线程的池子(一种存放线程的容器),一个线程池中可以保存多个线程。
主要负责保存线程和给线程分配任务
线程池的工作原理:提前创建指定个数线程,然后添加多个任务,线程池自动将任务分配线程去执行

2.使用线程池

1)创建线程池对象
线程池对象 = ThreadPoolExecutor(最大线程数)

2)添加任务
a. 一次添加一个任务(任务对应的函数的参数的个数可以是任意多个)
线程池对象.submit(函数名, 实参1, 实参2, 实参3,…)
返回值是一个future对象,future对象.result()可以得到任务对应的函数的返回值

a. 一次添加多个任务(任务对应的函数必须是有且只有一个参数的函数)
线程池对象.map(函数名, 序列)
返回值是一个生成器,生成器中的元素是函数的返回值

3)关闭线程池
线程池关闭不影响已经添加的任务,只是让线程池不能再添加新的任务
线程池.shutdown()除了可以关闭线程池,还具备等待线程池中所有的任务都完成的功能
参数wait - 是否等待线程任务全部结束,默认是True


import time
from datetime import datetime
from random import randint
from concurrent.futures import ThreadPoolExecutor


def func1():
    print('函数1')
    return 111

def func2(a):
    print(f'函数2:{a}')
    return 222


def func3(x, y):
    print(f'函数3:{x}, {y}')
    return 333


def download(name):
    print(f'{name}开始下载:{datetime.now()}')
    time.sleep(randint(2, 6))
    print(f'{name}下载结束:{datetime.now()}')


if __name__ == '__main__':
    # 1. 创建线程池
    pool = ThreadPoolExecutor(10)

    # 2. 添加任务(任务添加后会马上进入等待被执行的阶段)
    # 1) 任务一个一个的添加
    result1 = pool.submit(func1)
    result2 =pool.submit(func2, 10)
    result3 =pool.submit(func3, 100, 200)
    # 2)一次性添加多个任务
    result4 = pool.map(func2, [10, 20, 30, 40])
    result5 = pool.map(download, ['肖生克的救赎', '阿甘正传', '霸王别姬'])

    # 3)关闭线程池
    # 线程池关闭不影响已经添加的任务,只是让线程池不能再添加新的任务
    # 线程池.shutdown()除了可以关闭线程池,还具备等待线程池中所有的任务都完成的功能
    # 参数wait - 是否等待线程任务全部结束,默认是True
    pool.shutdown()

    print('==================')

    # 4) 获取结果(了解)
    print(result1.result(), result2.result(), result3.result())

    print(result4, result5)
    for x in result4:
        print(x)

进程池

# 导入构建进程池的函数: Pool
from multiprocessing import Pool
import time
from random import randint

def func1():
    print('函数1开始执行')
    time.sleep(randint(1, 3))
    print('函数1执行完成')


def func2(x, y):
    print(f'函数2开始执行:{x, y}')
    time.sleep(randint(1, 3))
    print('函数2执行完成')


def func3(x):
    print(f'函数3开始执行:{x}')
    time.sleep(randint(1, 3))
    print('函数3执行完成')


if __name__ == '__main__':
    # 1. 创建进程池,指定进程数量
    pool = Pool(5)

    # 2. 添加任务
    """
    1)一次添加一个任务
    进程对象.apply(函数, 实参元组)    -   用这个函数添加的多个任务在进程池中同步(串行)执行
    进程对象.apply_async(函数, 实参元组)   -  用这个函数添加的多个任务在进程池中异步(并行)执行
    
    2)一次添加多个任务
    进程池对象.map(函数, 序列)   -   同时添加多个任务异步执行,添加完以后不用close和join
    进程池对象.map_async(函数, 序列)  -  同时添加多个任务异步执行,添加完以后必须使用close和join

    注意:如果用带async的方法添加任务,任务添加完以后必须用进程池调用close方法,再调用join方法,
         任务才会启动
    """
    # 一次添加一个任务
    # pool.apply_async(func1)
    # pool.apply_async(func2, (100, 200))

    pool.map(func3, range(100, 10000, 100))

    # 3.关闭进程池
    # pool.close()
    # 4.等待进程池中的任务全部完成
    # pool.join()

线程和进程使用总结

1. 概念:
线程
进程
线程池
进程池(几乎不用)

2. 使用的方案
1)单纯使用多个线程(Thread)
2)单纯使用多个进程(Process)
3)子进程中有子线程(Process、Thread)
4)线程池(ThreadPoolExecutor)
5)子进程中线程池(Process、ThreadPoolExecutor)

网站信息爬取练习

import requests
from concurrent.futures import ThreadPoolExecutor
from bs4 import BeautifulSoup
from re import sub
from multiprocessing import Process, Queue
import csv

q = Queue()


def get_html(page):
    # 获取数据
    url = f'https://cd.zu.ke.com/zufang/pg{page}/#contentList'
    response = requests.get(url)

    # 解析数据
    soup = BeautifulSoup(response.text, 'lxml')
    house_divs = soup.select('.content__list>.content__list--item')
    houes_data = []
    for house in house_divs:
        # 名字
        name = house.select_one('.twoline').text.strip()
        # 价格
        price = house.select_one('.content__list--item-price').text.replace(' ', '')
        message = house.select_one('.content__list--item--des').text
        message = sub(r'\s+', '', message).split('/')
        # 标签
        if len(message) == 6:
            tag = message[0]
        else:
            tag = ''
        # 位置
        address = message[-5]
        region = address.split('-')[0]
        # 面积
        area = message[-4]
        # 方位
        orientation = message[-3]
        # 户型
        house_type = message[-2]
        # 楼层
        floor = message[-1]
        houes_data.append([name, price, region, address, area, orientation, house_type, floor, tag])

    q.put(houes_data)


def save_data(q: Queue):
    writer = csv.writer(open('files/租房信息.csv', 'w', newline=''))
    writer.writerow(['标题', '价格', '区域', '地址', '面积', '方位', '户型', '标签'])
    while True:
        house_data = q.get()
        if house_data == 'end':
            break
        writer.writerows(house_data)
        print('一页数据写入成功!')


if __name__ == '__main__':
    pool = ThreadPoolExecutor(10)
    pool.map(get_html, range(1, 101))

    p = Process(target=save_data, args=(q,))
    p.start()

    pool.shutdown()
    q.put('end')

数据处理练习

import csv

# 练习:统计不同区域租房的平均价格(每平米)
# 区域   均价
# 成华   20元/平米
# 青羊   35元/平米

if __name__ == '__main__':
    reader = csv.DictReader(open('files/租房信息.csv', encoding='utf-8', newline=''))
    # {'高新': [20, 34], '双流': [12, 20, 30, 12]}
    data = {}
    for x in reader:
        region = x['区域']
        price = float(x['价格'][:-3])
        area = float(x['面积'][:-1])
        one_price = price / area
        all_price = data.get(region, [])
        all_price.append(one_price)
        data[region] = all_price
    new_data = {x: f'{sum(data[x])/len(data[x]):.2f}元/㎡' for x in data}
    print(new_data)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值