同一个进程中的多个线程之间数据可以直接共享
deffunc1():global a
a =100
data.append(10)deffunc2():global b
b =200print(a)
data.append(20)if __name__ =='__main__':
data =[]
t1 = Thread(target=func1)
t2 = Thread(target=func2)
t1.start()
t2.start()print(a, b)print(data)# 多进程基本用法from multiprocessing import Process, current_process
from datetime import datetime
import time, random
02、多进程的使用
一个程序默认只有一个进程,如果需要多个进程,需要程序员手动创建进程对象:
进程对象 = Process(target=函数, args=元组)
进程对象.start()
进程对象.join()
defdownload(name):print(f'======{name}开始下载:{datetime.now()}')print(current_process())
time.sleep(random.randint(2,7))print(f'======{name}下载结束:{datetime.now()}')# 多进程程序中下面这个if语句必须添加if __name__ =='__main__':
p1 = Process(target=download, args=('肖申克的救赎',))
p2 = Process(target=download, args=('阿甘正传',))
p3 = Process(target=download, args=('霸王别姬',))
p1.start()
p2.start()
p3.start()
p1.join()
p2.join()
p3.join()print('全部下载完成!')# 不同进程中的数据无法直接共享deffunc1():global a
a =100
data.append(10)deffunc2():global b
b =200# print(a)
data.append(20)if __name__ =='__main__':
data =[]
p1 = Process(target=func1)
p2 = Process(target=func2)
p1.start()
p2.start()
p1.join()
p2.join()print(data)
2、线程池
from threading import Thread, current_thread
from concurrent.futures import ThreadPoolExecutor
import time
from random import randint
from datetime import datetime
defdownload(name):print(f'======{name}开始下载:{datetime.now()}')print(current_thread())
time.sleep(randint(2,7))print(f'======={name}下载结束:{datetime.now()}')1. 没有线程池(了解)if __name__ =='__main__':# 下载1000个电影,同时可以有10个一起下载for count inrange(100):
ts =[]for x inrange(10):
t = Thread(target=download, args=(f'电影{count*10+x}',))
t.start()
ts.append(t)for t in ts:
t.join()print(f'{count}波完成')# 2. 线程池的使用# 1)创建线程池对象
pool = ThreadPoolExecutor(5)# 2)添加任务# a.一次添加一个任务: 线程池对象.submit(函数,实参1,实参2,...)
pool.submit(download,'肖申克的救赎')for x inrange(10):
pool.submit(download,f'电影{x}')# b.一次添加多个任务: 线程池对象.map(函数, [数据1, 数据2, 数据3,...])# 注意:这个地方任务对应的函数必须是有且只有一个参数的函数
pool.map(download,['霸王别姬','阿甘正传','触不可及'])# 注意:只要线程池没有关闭,我们可以在任何你需要的位置添加任务。# 3)关闭线程池(同时具备关闭线程池和等待线程池任务都完成的功能)
pool.shutdown()# pool.submit(download, '电影10') # 报错!print('全部任务都完成!')
3、线程池亚马逊
import requests
from bs4 import BeautifulSoup
from concurrent.futures import ThreadPoolExecutor
defget_goods_list(page, name='防晒霜'):"""
获取一页商品列表
:param page: 页数,从1开始
:param name: 商品名称
:return:
"""# 1. 获取网页数据
url =f'https://www.amazon.cn/s?k={name}&page={page}'
headers ={'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36'}
response = requests.get(url, headers=headers)# 2. 解析数据
soup = BeautifulSoup(response.text,'lxml')
goods_div_list = soup.select('.s-result-item')
all_url =[]for div in goods_div_list:
goods_url = div.attrs['data-asin']if goods_url:
all_url.append('https://www.amazon.cn/dp/'+ goods_url)print(f'第{page}页数据获取完成')return all_url
defget_goods_info(goods_url):"""
获取指定地址对应的商品信息
:param goods_url: 商品详情地址
:return: None
"""print('----商品详情开始获取----')
headers ={'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36'}
response = requests.get(goods_url, headers=headers)
soup = BeautifulSoup(response.text,'lxml')
title = soup.select_one('#productTitle').text.strip()
price = soup.select_one('.a-price>.a-offscreen').text.strip()print(title, price)if __name__ =='__main__':# 1. 创建两个线程池
list_pool = ThreadPoolExecutor(5)# 获取所有商品列表数据的线程池
info_pool = ThreadPoolExecutor(50)# 获取商品详情数据的线程池# 2. 添加任务# map函数的返回值是一个生成器,生成器中的数据就是任务函数的返回值
result = list_pool.map(get_goods_list,range(1,11))for x in result:
info_pool.map(get_goods_info, x)
4、线程队列
from threading import Thread
from concurrent.futures import ThreadPoolExecutor
import time
from datetime import datetime
from random import randint
# 1. 线程队列的使用from queue import Queue
"""
使用队列的方法:
1)创建队列对象:Queue()
2)添加数据:队列对象.put(数据)
3)获取数据:队列对象.get() 、队列对象.get(timeout=超时时间)
如果队列是空,队列的get操作不会报错,并且会进入等待状态,等到有数据进入队列为止
"""defdownload(name):print(f'----{name}开始下载----')
time.sleep(randint(2,7))print(f'----{name}下载结束----', datetime.now())# 电影数据# all_data.append(f'{name}数据')# return f'{name}数据'
q.put(f'{name}数据')if __name__ =='__main__':# 方法1的列表
all_data =[]# 方法3的队列
q = Queue()
pool = ThreadPoolExecutor(5)
result = pool.map(download,[f'电影{x}'for x inrange(20)])# ====在主线程获取所有的下载结果====# ------------------方法1 - 不管是线程池还是直接使用多个线程都有效------------# 1)在主线程中创建列表,然后在子线程中直接往列表中添加数据# 2)等所有任务都完成后获取数据# pool.shutdown()# print(all_data)# ------------------方法2 - 线程池有效------------# 1)将子线程中产生的数据作为函数的返回值# 2)获取map函数的返回值(返回值是一个生成器,生成器中的元素就是每次调用函数,函数的返回值)# for x in result:# print(x, datetime.now())# ----------------方法3 - 线程池和直接使用多线程都有效-----------# 1)在主线程中创建一个队列# 2)在子线程中往队列中添加数据# 3)在主线程中获取队列中的数据for _ inrange(20):print(q.get(), datetime.now())
5、队列的使用方法
from queue import Queue
from threading import Thread
# q = Queue()# # q.put(100)# print(q.get())## print('end!')deffunc1():
value =input('请输入数据:')
q.put(value)if __name__ =='__main__':
q = Queue()
t = Thread(target=func1)
t.start()print(q.get())print('end!')