我想在Python中使用多线程和队列(以限制线程数)来迭代字典中的字典(模拟目录或网站的结构)。我创建了 mainDict 来模拟这个
mainDict = {"Layer1": {"Layer11": 1, "Layer12": 1, "Layer13": 1, "Layer14": 1, "Layer15": 1," Layer16": 1},
"Layer2": {"Layer21": 2, "Layer22": 2, "Layer23": 2, "Layer24": 2, "Layer25": 2, "Layer26": 2},
"Layer3": {"Layer31": 4, "Layer32": 4, "Layer33": 4, "Layer34": 4, "Layer35": 4, "Layer36": 4},
"Layer4": {"Layer41": 8, "Layer42": 8, "Layer43": 8, "Layer44": 8, "Layer45": 8, "Layer46": 8},
"Layer5": {"Layer51": 16, "Layer52": 16, "Layer53": 16, "Layer54": 16, "Layer55": 16, "Layer56": 16},
"Layer6": {"Layer61": 32, "Layer62": 32, "Layer63": 32, "Layer64": 32, "Layer65": 32, "Layer66": 32}}
还有一个 Crawler 类,用于为 mainDict 的每个第一个子词典实例化一个爬虫。
我的想法是,我想创建 2 个可以爬行到 Layer(i) (i=1..6) 的线程(一次创建有限数量的线程/爬虫,以减少 CPU 使用率)。每个线程都会爬行,直到到达“树”的叶子,然后移动到下一个字典(例如,爬虫 0 将经过 Layer1,爬虫 1 将经过 Layer2,完成后将经过 Layer3...)。
class Crawler:
def __init__(self, rootDict, number_of_delay, crawler):
self.crawler = crawler
self.rootDict = rootDict
self.number_of_delay = number_of_delay
def crawlAllLeaves(self, myDict):
for k, v in myDict.items():
if isinstance(v, dict):
print("Crawler {} is crawling {}".format(self.crawler, k))
self.crawlAllLeaves(v)
else:
print("Crawler {} reached the value {} for key {}".format(self.crawler, v, k))
time.sleep(self.number_of_delay + v)
def someAuxFunc():
#to simulate some loading time
time.sleep(2)
def createWorker(q, delayNumber, crawler):
tc = Crawler(mainDict[q.get()], delayNumber, crawler)
tc.crawlAllLeaves(tc.rootDict)
def threader(q, delayNumber, crawler):
while True:
print("crawler {}: has gotten the url {}".format(crawler, q.get()))
createWorker(q, delayNumber, crawler)
print("crawler {}: has finished the url {}".format(crawler, q.get()))
q.task_done()
q = Queue()
number_of_threads = 2
delayNumber = 2
for thread in range(number_of_threads):
th = threading.Thread(target=threader, args=(q, delayNumber, thread,))
th.setDaemon(True)
th.start()
for key, value in mainDict.items():
someAuxFunc()
print("QUEING {}".format(key))
q.put(key)
q.join()
我有两个问题:
- 它只创建 2 个线程并获取队列的前 2 个元素(子词典),然后它不执行任何操作,甚至退出;它保持悬挂状态
- 在 threader() 函数中,它表示它将获得一个子词典,但会迭代另一个不同的词典,如crawlAllLeaves() 中的 print 所示
你能帮我解决这个问题吗,因为我想学习 Python 和线程,但我不知道我做错了什么?
最佳答案
您的问题在于队列的处理,它解释了您的两个问题。您继续从队列中读取数据,而不是使用从队列中实际收到的值。看看这个(固定的)代码:
def createWorker(bar, delayNumber, crawler):
tc = Crawler(mainDict[bar], delayNumber, crawler)
tc.crawlAllLeaves(tc.rootDict)
def threader(q, delayNumber, crawler):
while True:
foo = q.get()
print("crawler {}: has gotten the url {}".format(crawler, foo))
createWorker(foo, delayNumber, crawler)
print("crawler {}: has finished the url {}".format(crawler, foo))
q.task_done()
在您的 threader
中我们现在将队列读取一次到一个变量,然后将该变量传递给 createWorker
。在你的createWorker
您使用这个值而不是获取另一个值。
您的原始代码最初在第一个打印语句中从队列中获取一个值。它打印该值然后丢弃。然后你调用createWorker
,您可以从队列中接收下一个值并开始处理该值。最后,第二个打印语句从队列中获取另一个值并打印它。 print 语句中显示的值实际上都没有传递给 createWorker
。
Queue.get()
如果那里没有任何东西,则默认阻止。当您为每个处理的值获取三个值时,您的结果是无论是什么,但绝对不是您想要的结果。你的代码块在最后 q.join()
中正如您所使用的 get()
三次从队列中获取值但使用 task_done
只有一次。因此,您的连接会阻塞,因为它假设仍有任务正在进行中。
关于python - 在 Python 中使用多线程迭代字典,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46711169/