在Python中使用多线程迭代字典



我想使用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个线程(一次只能创建有限数量的线程/爬网程序,以减少CPU使用量),这些线程可以爬网到第(I)层(I=1..6)。每个线程都将爬网,直到它到达"树"的叶子,然后移到下一个字典(例如,爬网程序0将经过第1层,爬网程序1将经过第2层,在完成第3层之后…)。

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()

我有两个问题:

  1. 它只创建2个线程,并获取队列的前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中,您使用此值,而不是获取另一个值。

最初,您的原始代码从第一个print语句中的队列中获取一个值。它打印值,然后丢弃。然后调用createWorker,从中接收队列中的下一个值并开始处理。最后,第二个print语句从队列中获取另一个值并打印该值。print语句中显示的值实际上都没有传递给createWorker

如果没有任何内容,则默认情况下Queue.get()会阻塞。当你为每处理一个得到三个值时,你的结果就是它是什么,但绝对不是你想要的。你的代码在最后的q.join()中阻塞,因为你已经使用了三次get()从队列中获得一个值,但只使用了一次task_done。因此,您的连接会阻塞,因为它假定仍有任务在进行中。

最新更新