01、线程为什么需要通信
--线程会存在问题
02、多线程使用全局变量进行通信:使用上一个博客爬虫案例作为讲解案例
--为什么需要通信:
--线程01:爬虫一个线程用于爬取主界面的url
--线程02:取出爬取到的url进行解析进一步获取页面html详情
--使用 global定义全局变量【共享变量】 || 使用参数传值的方式也行,因为python传递的是对象的引用
import threading
import time
url_list = []
def get_detail_html():
# 根据获取到的url进一步访问界面,得到详细的html代码
global url_list
while True:
if len(url_list):
url = url_list.pop()
print("get detail html_01 start")
print(url)
print("get detail html_01 and")
else:
break
def get_detail_url():
# 获取主界面中的列表url,等待进一步访问
global url_list
while True:
print("get detail html_02 start")
for i in range(20):
url_list.append("http://www.fund{}".format(i))
print("get detail html_02 and")
if __name__ == '__main__':
# 获取url
thread_url = threading.Thread(target=get_detail_url)
thread_url.start()
# 多线程打印url
for i in range(10):
thread_html = threading.Thread(target=get_detail_html)
thread_html.start()
03、多线程使用Queue进行通信
--除非你对 线程锁 足够了解,否则不推荐使用共享变量
--Queue:这里推荐使用,它可以使我们的线程按照规定的同步方式进行交互和通信
--Queue对象及其属性都是线程安全的
--线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
--线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据
--查看Queue的方法:点开pycharm左边和project一列的structure,可以看到对象的结构、变量和属性
--put:put方法是往queue中压入数据
--get: get方法是从queue中取出对象,Queue.get()时如果queue为空会使线程阻塞在此处,直至获取到数据
--qsize: 获取队列的长度 Queue.qsize()
--put_nowait 和 get_nowait:在从队列取数据 和 往队列放数据时,无需等待其执行结束就返回,这是一个异步方法
--Queue.put(item, block=True, timeout=None):
--block: 表示设置是否等待队列操作完毕
--timeout: 表示等待时间多长以后即刻返回
--使用队列进行通信的代码示例:
import threading
import time
from queue import Queue
def get_detail_html(queue):
# 根据获取到的url进一步访问界面,得到详细的html代码
while True:
url = queue.get()
print("get detail html_01 start")
print(url)
print("get detail html_01 and")
def get_detail_url(queue):
# 获取主界面中的列表url,等待进一步访问
while True:
print("get detail html_02 start")
for i in range(20):
queue.put("http://www.fund{}".format(i))
print("get detail html_02 and")
if __name__ == '__main__':
# 申明队列 queue
details_queue = Queue(maxsize=1000)
# 获取url
thread_url = threading.Thread(target=get_detail_url, args=(details_queue,))
thread_url.start()
# 多线程打印url
for i in range(10):
thread_html = threading.Thread(target=get_detail_html, args=(details_queue,))
thread_html.start()
--成对使用 queue.task_done() 和 queue.join(),对程序进行控制
import threading
import time
from queue import Queue
len_url = 0
len_out = 0
def get_detail_html(queue):
# 根据获取到的url进一步访问界面,得到详细的html代码
while True:
global len_out
if queue.qsize() > 0:
url = queue.get()
print(url)
len_out += 1
print("len_out: {}".format(len_out))
else:
break
queue.task_done()
print("获取详情页结束啦!!!!!!!!!")
def get_detail_url(queue):
# 获取主界面中的列表url,等待进一步访问
while True:
global len_url
if len_url <= 1000:
for i in range(1001):
queue.put("http://www.fund{}".format(i))
len_url += 1
print("len_url: {}".format(len_url))
else:
break
queue.join()
print("生产者也结束啦###################")
if __name__ == '__main__':
# 申明队列 queue
details_queue = Queue(maxsize=1000)
# 获取url
thread_url = threading.Thread(target=get_detail_url, args=(details_queue,))
thread_url.start()
# 多线程打印url
for i in range(10):
thread_html = threading.Thread(target=get_detail_html, args=(details_queue,))
thread_html.start()
# # 队列阻塞
# details_queue.task_done()
# details_queue.join()