2017-10-20 16 views
0

Contexte:objet de partage entre les fils produit NoneType

Je travaille sur un robot web qui génère 7 fils de discussion, chaque requête à une URL unique pour un fichier XML. Lorsque chaque requête reçoit une réponse, il se que la réponse dans un arbre XML comme ceci:

conn = http.client.HTTPSConnection(host = uHost, port = uPort) 
conn.request('GET', url = '/some/url/file.xml') 
resp = conn.getresponse() 
tree = xml.etree.ElementTree.parse(resp) 

Lorsque chaque thread est démarré, il est donné un queue.Queue() comme argument, pour qu'il puisse mettre le tree en si __main__ est le seul thread à écrire des fichiers. En continuant d'en haut:

__main__

def receive(q): 
    while True: 
     try: 
      uTree = q.get() 
      uTree.write('/some/path/file.xml') 
     except queue.Empty: 
      pass 

a donné naissance à

conn = http.client.HTTPSConnection(host = uHost, port = uPort) 
conn.request('GET', url = '/some/url/file.xml') 
resp = conn.getresponse() 
tree = xml.etree.ElementTree.parse(resp) 
q.put_nowait(tree) 

Cependant, je commencé à recevoir AttributeError: 'NoneType' object has no attribute 'write' lors de l'appel uTree.write(). Un changement rapide de uTree.write()-print(type(uTree)) a montré que les objets resteraient parfois intactes, d'autres fois ils deviennent NoneType:

<class 'xml.etree.ElementTree.ElementTree'> 
<class 'xml.etree.ElementTree.ElementTree'> 
<class 'xml.etree.ElementTree.ElementTree'> 
<class 'xml.etree.ElementTree.ElementTree'> 
<class 'NoneType'> 
<class 'NoneType'> 
<class 'xml.etree.ElementTree.ElementTree'> 
<class 'xml.etree.ElementTree.ElementTree'> 

Questions:

Pourquoi les objets sont passés d'un threading.Thread() à un queue.Queue() [résidant sur __main__ ] passer à NoneType de manière incohérente?

Comment puis-je résoudre ce problème?

complet Code (le cas échéant):

main.py

import queue 
import crawl # custom module 
import threading 

def crawler(query): 
    while True: 
     try: 
      query.connect() 
      break 
     except: 
      pass 

def receive(q): 
    while True: 
     try: 
      uQuery = q.get() 
      uTree = uQuery.tree 
      uTree.write('/some/path/file.xml') 
     except queue.Empty: 
      pass 

urls = ['/url1.xml', '/url2.xml', ...] 

q = queue.Queue() 

queries = [Query(url, q) for url in urls] 
threads = [threading.Thread(target = crawler, args = (query,)) for query in queres] 

for t in threads: 
    t.start() 

receive(q) 

crawl.py

import http.client 
import xml.etree.ElementTree as ET 

class Query: 
    def __init__(self, url, q): 
     self.url = url 
     self.queue = q 
     self.tree = None 

    def connect(): 
     conn = http.Client.HTTPConnect(host = 'something.com', port = '80') 
     conn.request('GET', url = self.url) 
     resp = conn.getresponse() 
     self.tree = ET.parse(resp) 
     self.queue.put_nowait(self) 
     conn.close() 

Répondre

0

(je ne suis pas commenter, mais semblent avoir la réputation)

Ceci ne résout pas votre problème mais peut vous donner quelques indications.

Je sais qu'il est plus difficile de déboguer les problèmes de threading mais je suggère de simplifier votre exemple. Vous incluez l'analyse de XML à l'aide des connexions ElementTree et HTTP - les deux ne semblent pas être pertinents pour la question.

Pour résoudre votre problème, vous pouvez également obtenir des informations en enregistrant ce que vous mettez dans la file d'attente.

Je vous conseille d'être très prudent lorsque vous placez des objets complexes, tels qu'un arbre analysé, dans la file d'attente. Vous devrez ensuite vous assurer que le type d'objet est lui-même thread-safe.

Si vous n'êtes pas au courant, je vous recommande d'utiliser https://scrapy.org/ ce qui rendrait l'implémentation d'un robot d'exploration beaucoup plus facile.