线程池和进程池

线程队列

from queue import Queue

# queue模块中的队列,只能保存一般数据或者多线程中产生的数据(多用于多线程,自带线程安全属性),但是不能用来存储多进程中产生的数据。
# 队列数据结构:是容器,先进先出

if __name__ == '__main__':
    # 1. 队列基本用法
    # 1) 创建队列对象: Queue()
    q = Queue()
    # 2) 添加数据(进): 队列对象.put(数据)
    q.put(100)
    q.put(200)

    # 3) 获取数据(出) - 出一个就少一个: 队列对象.get()
    print('个数:', q.qsize())
    print(q.get())
    print('个数:', q.qsize())
    print(q.get())
    print('个数:', q.qsize())

    # 4) 获取队列中元素的个数: 队列对象.qsize()
    # 5) 通过get获取数据的时候如果队列中没有数据,get方法会等待,直到队列中有数据,或者超时为止
    # 队列对象.get(timeout=超时时间)
    # q.get()

队列在多线程中的用法

from threading import Thread
from queue import Queue, Empty
import time
from random import randint


def download(name):
    print(f'{name}开始下载')
    time.sleep(randint(2, 9))
    print(f'{name}下载结束')
    q.put(f'{name}数据')


def del_data():
    while True:
        data = q.get()
        if data == 'end':
            break
        print(f'------------处理{data}------------')


if __name__ == '__main__':
    q = Queue()

    # 创建一个线程处理数据(方式3对应的代码)
    del_t = Thread(target=del_data)
    del_t.start()

    # 创建子线程下载数据
    names = [f'电影{x}' for x in range(1, 11)]
    ts = []      # 保存下载电影的所有线程对象
    for name in names:
        t = Thread(target=download, args=(name,))
        t.start()
        ts.append(t)

    # 1. 获取队列数据方式1:能做到子线程得到数据,主线程马上就处理数据,但是数据处理完程序没法结束
    # while True:
    #     data = q.get()
    #     print(f'------------处理{data}------------')

    # 2. 获取队列数据方式2: 能做到子线程得到数据,主线程马上就处理数据,通过是否超时来判断数据是否处理完成
    # while True:
    #     try:
    #         data = q.get(timeout=5)
    #         print(f'------------处理{data}------------')
    #     except Empty:
    #         break

    # 3. 在所有下载数据的线程都结束的时候在队列中添加结束标记,在子线程中去获取队列中来处理
    for t in ts:
        t.join()

    q.put('end')

进程队列

from multiprocessing import Process, Queue, current_process
import time
from random import randint
from threading import Thread
# from queue import Queue

# 进程队列
"""
1)基本操作:
创建队列对象:Queue()
添加数据: 队列对象.put(数据)
获取数据: 队列对象.get() / 队列对象.get(timeout=超时时间)   

2)注意事项:
如果想要使用一个队列对象获取不同进程中的数据,这个队列必须通过关联进程的函数的参数传递到进程中
"""


def download(name, q:Queue):
    print(f'{name}开始下载')
    time.sleep(randint(2, 9))
    print(f'{name}下载结束')
    q.put(f'{name}数据')


def del_data(q: Queue):
    while True:
        data = q.get()
        if data == 'end':
            break
        print(f'--------------处理{data}----------------')


if __name__ == '__main__':

    # 2. 在子进程中处理数据
    # 2.1 创建队列对象,并且传递到子进程中
    queue = Queue()
    del_p = Process(target=del_data, args=(queue,))
    del_p.start()

    # 1. 下载电影
    names = [f'电影{x}' for x in range(1, 11)]
    ps = []
    for name in names:
        p = Process(target=download, args=(name, queue))
        p.start()
        ps.append(p)

    # 2.2 等到所有的进程任务都结束在队列添加结束标记
    for p in ps:
        p.join()

    queue.put('end')

线程池

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


def download(name):
    print(f'{name}开始下载')
    print(current_thread())
    time.sleep(1)
    print(f'{name}下载结束')


# 线程池的工作原理:提前创建指定个数的线程,保存到一个线程池中。
#               然后再往线程池中添加若干个任务,线程池自动为线程分配任务。

# 1. 创建线程池,确定线程池中线程的个数
pool = ThreadPoolExecutor(max_workers=50)

# 2. 往线程池中添加添加任务
names = [f'电影{x}' for x in range(100)]

# 1) 一次添加一个任务
# 线程池.submit(函数, 参数1, 参数2,...)
# pool.submit(download, '电影0')
# print('开始时间:', datetime.now())
# for name in names:
#     pool.submit(download, name)

# 2) 一次添加多个任务
# 线程池.map(函数, 包含所有任务的参数的序列)
pool.map(download, names)

pool.submit(download, 'hello!')

# 3.关闭线程池
# 线程池.shutdown()   -   关闭线程池以后,线程池无法再添加任务,但是不影响已经添加的任务的执行
pool.shutdown()
# pool.submit(download, '电影100')

线程池使用的细节

from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED, as_completed
import time
from random import randint


def download(name, x):
    print(f'{name}-{x}开始下载')
    time.sleep(randint(1, 4))
    print(f'{name}-{x}下载结束')
    return f'{name}数据'


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

    # 2.添加任务
    # 线程池.submit(函数)  -  函数可以是有任意多个参数的函数; 返回值是一个可操作的future对象(任务对象)
    # 线程池.map(函数)   -  函数只能是有且只有一个参数的函数; 返回值没法控制和操作
    f = pool.submit(download, '电影1', 10)

    all_task = [pool.submit(download, f'电影{x}', x*10) for x in range(100)]

    # all_task = []
    # for x in range(100):
    #     f = pool.submit(download, f'电影{x}', x*10)
    #     all_task.append(f)

    # 3.等待任务完成
    # wait(all_task, return_when=ALL_COMPLETED)
    # print('--------------------电影下载完成--------------------')


    # 4. 获取任务函数的返回值
    # 按照任务完成的先后顺序获取任务,并且获取已经完成的任务的结果(函数返回值)
    for task in as_completed(all_task):
        print(f'------------{task.result()}------------------')

    print('--------------------电影下载完成--------------------')

进程池

from multiprocessing import Pool
import time
from random import randint


def download(name):
    print(f'{name}开始下载')
    time.sleep(randint(1, 4))
    print(f'{name}下载结束')
    return f'{name}数据'


if __name__ == '__main__':
    # 1. 创建进程池对象: Pool(进程数)
    pool = Pool(10)

    # 2. 添加任务
    # 1) 一次添加一个任务
    """
    a. 进程池对象.apply(函数, 参数)      -       同步(串行);进程池中的多个任务串行执行
    b. 进程池对象.apply_async(函数, 参数)    -   异步执行(并行);必须配合close和join一起用
    
    函数 - 任务对应的函数的函数名
    参数 - 元组;调用任务函数的时候的实参,需要多少个实参,元组中就给多少个元素
    """
    # for x in range(20):
    #     pool.apply_async(download, (f'电影{x}',))

    # 2) 同时添加多个任务
    """
    进程池.map(函数, 参数序列)   -   序列中有多少个元素就添加多少个任务;
                                  进程池中的任务并行, 进程池中的任务和主进程串行
                                  
    进程池.map_async(函数, 参数序列)     -      进程池中的任务和主进程并行执行
    
    map和map_async的返回值是所有任务对应的函数的返回值
    """
    result = pool.map_async(download, [(f'电影{x}', x*10) for x in range(20)])

    print('--------------主进程-------------------')

    # 3. 关闭进程池,阻止往进程池中添加新的任务
    pool.close()

    # 4.等待进程池中的任务都结束
    pool.join()
    print('---------------下载完成--------------------')

    # 获取函数返回值
    print(result.get())

线程池爬51job

import requests
from re import findall
from json import loads
from concurrent.futures import ThreadPoolExecutor, as_completed, wait
import csv


def get_one_page(page):
    headers = {
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'
    }
    url = f'https://search.51job.com/list/000000,000000,0000,00,9,99,python,2,{page}.html?lang=c&postchannel=0000&workyear=99&cotype=99&degreefrom=99&jobterm=99&companysize=99&ord_field=0&dibiaoid=0&line=&welfare='
    response = requests.get(url, headers=headers)
    json_data = findall(r'window.__SEARCH_RESULT__ =(.+?)</script>', response.text)[0]
    dict_data = loads(json_data)
    page_data = []
    for job in dict_data['engine_search_result']:
        page_data.append({
            '工作名称': job['job_name'],
            '工作详情': job['job_href'],
            '薪资': job['providesalary_text'],
            '工作地点': job['workarea_text'],
            '工作年限': job['workyear'],
            '福利待遇': job['jobwelf'],
            '公司': job['company_name']
        })
    # print(page_data)
    return page_data


if __name__ == '__main__':

    # 1. 使用线程池下载数据
    pool = ThreadPoolExecutor(20)
    all_task = [pool.submit(get_one_page, page) for page in range(1, 101)]

    # 2. 在主线程处理数据
    f = open('files/python.csv', 'a', encoding='utf-8')
    writer = csv.DictWriter(f, ['工作名称', '工作详情', '薪资', '工作地点', '工作年限', '福利待遇', '公司'])
    writer.writeheader()
    for t in as_completed(all_task):
        print('----------处理一页数据---------')
        writer.writerows(t.result())
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值