线程池和进程池与队列
线程队列
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.get())
print(q.get())
# 4)获取队列中元素的个数:队列对象.qsize()
q.qsize()
# 5)通过格填获取数据的时候如果队列中没有数据,get方法会等待,直到队列中有数据,或者超时报错
# 队列对象.get(timeout=超时时间)
q.get(timeout=5)
队列在多线程中的用法
from threading import Thread
from queue import Queue, Empty
import time
from random import randint
def download(name):
print(f'{name}开始下载')
time.sleep(randint(3, 9))
print(f'{name}下载结束')
q.put(f'{name}数据')
def del_data():
while 1:
data = q.get()
if data == 'end':
break
else:
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)]
all_t = []
for name in names:
t = Thread(target=download, args=(name,))
t.start()
all_t.append(t)
# 1.获取队列数据方式1:能做到子线程得到数据,主线程马上就处理数据,但是数据处理完程序没法结束
# while 1:
# data = q.get(timeout=5)
# print(f'-----处理数据{data}-------')
# 2.获取队列数据方式2:能做到子线程得到数据,主线程马上就处理数据,通过是否超时来判断数据是否处理完成数据处理完程序没法结束
# while 1:
# try:
# data = q.get(timeout=5)
# print(f'-----处理数据{data}-------')
# except Empty:
# break
for t in all_t:
t.join()
q.put('end')
进程队列
from multiprocessing import Process, Queue, current_process
import time
from random import randint
# 进程队列
"""
1)基本操作
创建队列对象:Queue()
添加数据:队列对象.put(数据)
获取数据:对列对象.get()/队列对象.get(timeout=超时时间)
2)注意事项:
如果想要使用一个队列对象获取不同进程中的数据,这个队列必须通过关联进程的函数的参数传递到进程中
"""
def download(name, q):
print(f'{name}开始下载')
time.sleep(randint(3, 9))
print(f'{name}下载结束')
q.put(f'{name}数据')
def del_data(q):
while 1:
data = q.get()
if data == 'end':
break
else:
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)]
all_q = []
for name in names:
q1 = Process(target=download, args=(name, queue))
q1.start()
all_q.append(q1)
# 2.2)等到所有的进程任务都结束在队列添加结束标记
for q in all_q:
q.join()
queue.put('end')
# 练习:使用多个进程同时下载多个电影,将下载的电影数据保存到队列中,在一个新的进程中去处理下载到的数据
# 做到:边下载边处理,下载完就处理完,处理完程序马上结束
线程池
import datetime
from concurrent.futures import ThreadPoolExecutor
import time
from multiprocessing.dummy import current_process
from random import randint
def download(name):
print(f'{name}开始下载')
print(current_process)
time.sleep(randint(3, 9))
print(f'{name}下载结束')
# 线程池的工作原理:提前创建指定个数的线程,保存到一个线程池中。
# 然后再往线程池中添加如干个任务,线程池自动为线程分配任务
# 1.创建线程池,确定线程池中线程的个数
pool = ThreadPoolExecutor(max_workers=10)
# 2.往线程池中添加任务
names = [f'电影{x}' for x in range(1, 100)]
# 1)一次添加一个任务
# 线程池.submit(函数,参数1,参数2,...)
# pool.submit(download, 'name')
# print('开始时间:', datetime.now())
# for name in names:
# pool.submit(download, name)
# 2)一次添加多个任务
# 线程池.map(函数,包含所以任务的参数的序列)
pool.map(download, names)
# 3.关闭线程池
# 线程池.shutdown() - 关闭线程池以后,线程无法在添加任务,但是不影响已经添加的任务的执行
pool.shutdown()
细节
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(3, 9))
print(f'{name}-{x}下载结束')
return f'{name}-{x}'
if __name__ == '__main__':
# 1.创建线程池
pool = ThreadPoolExecutor(max_workers=30)
# 2.添加任务
# 线程池.submit(函数) - 函数可以是任意有多个参数的函数;返回值是一个可操作的future对象
# 线程池.map(函数) - 函数只能是有且只有一个参数的函数;返回值没法控制和操作
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(5)
# 2.添加任务
# 1)一次添加一个任务
"""
a.进程池对象.apply(函数, 参数) - 同步(串行);进程池中的多个任务串行执行
b.进程池对象.apply_async(函数, 参数) - 异步执行(并行)
函数 - 任务对应的函数名
参数 - 元组:调用任务函数的时候的实参,需要多少个实参,元组中就给多少元素
"""
# for x in range(20):
# pool.apply_async(download, (f'电影{x}',))
"""
进程池.map(函数,参数序列) - 序列中有多个元素就添加多个任务;
进程池中的任务并行,进程池中的任务和主进程串行
进程池.map_async(函数,参数序列) - 进程池中的任务和主进程并行
"""
# 2)同时添加多个任务
result = pool.map_async(download, [f'电影{x}' for x in range(10)])
print('--------主进程--------')
# 3.关闭进程池,阻止进程中添加新的任务
pool.close()
# 4.等待进程池中的任务完成
pool.join()
print('---------下载完成-----------')
# 获取函数返回值
print(result.get())
线程池爬取网页
import csv
from re import findall
import requests
from json import loads
from concurrent.futures import ThreadPoolExecutor, wait, as_completed
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°reefrom=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', 'w', encoding='utf-8', newline='')
writer = csv.DictWriter(f, ['工作名称', '工作详情', '薪资', '工作地点', '工作年限', '福利待遇', '公司'])
writer.writeheader()
for t in as_completed(all_task):
print('----------处理一页数据---------')
writer.writerows(t.result())
进程池爬取网页
import requests
from re import findall
from json import loads
from multiprocessing import Pool
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°reefrom=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 = Pool(20)
all_task = pool.map_async(get_one_page, [page for page in range(1, 101)])
# 2.关闭进程池,阻止进程中添加新的任务
pool.close()
# 3.等待进程池中的任务完成
pool.join()
# 4. 在主进程处理数据
f = open('files1/python.csv', 'w', encoding='utf-8', newline='')
writer = csv.DictWriter(f, ['工作名称', '工作详情', '薪资', '工作地点', '工作年限', '福利待遇', '公司'])
writer.writeheader()
t = 0
while 1:
try:
print('----------处理一页数据---------')
writer.writerows(all_task.get()[t])
t += 1
except:
break