面试题答案
一键面试可能出现的资源竞争问题
在多线程网络爬虫中,多个线程同时访问和修改共享的URL队列时,可能出现以下资源竞争问题:
- 数据不一致:一个线程可能在读取URL队列中的一个URL后,还未来得及将其从队列中移除,另一个线程又读取了同一个URL,导致重复爬取。
- 队列损坏:多个线程同时对队列进行添加或移除操作,可能导致队列的内部数据结构损坏,例如索引错误或链表结构混乱。
使用锁机制解决问题
在Python中,可以使用threading.Lock
来解决资源竞争问题。Lock
对象有两种状态:锁定和未锁定。当一个线程获取到锁(将其状态设为锁定),其他线程就不能再获取,直到该线程释放锁(将其状态设为未锁定)。
代码示例
import threading
import queue
# 创建一个URL队列
url_queue = queue.Queue()
# 创建一个锁对象
lock = threading.Lock()
def crawler():
while True:
# 获取锁
lock.acquire()
try:
if not url_queue.empty():
url = url_queue.get()
print(f"线程 {threading.current_thread().name} 正在爬取: {url}")
finally:
# 释放锁
lock.release()
# 初始化URL队列
urls = ["url1", "url2", "url3", "url4", "url5"]
for url in urls:
url_queue.put(url)
# 创建并启动多个线程
num_threads = 3
threads = []
for i in range(num_threads):
t = threading.Thread(target=crawler)
threads.append(t)
t.start()
# 等待所有线程完成
for t in threads:
t.join()
在上述代码中:
- 首先创建了一个
Queue
对象url_queue
用于存储URL,以及一个Lock
对象lock
。 crawler
函数模拟爬虫线程的工作。在访问和修改url_queue
之前,先通过lock.acquire()
获取锁,确保同一时间只有一个线程可以操作队列。操作完成后,使用lock.release()
释放锁。- 初始化URL队列并创建多个线程,每个线程都执行
crawler
函数。 - 最后等待所有线程完成任务。通过这种方式,有效避免了多线程访问共享URL队列时可能出现的资源竞争问题。