2014-01-12 41 views
0

腳本的前幾行解釋了結構和機制。threading.Thread在調用httplib2.Http.request時卡住

我面臨的問題是執行卡在第53行。一旦下載程序在第一個請求上執行操作,它會正確生成api,但是在達到http_object.request(audioscrobbler_api)時它會卡住。

腳本在另一個系統上編碼和測試,並且得出正確的結果。

我可以確認httplib2軟件包沒有損壞,因爲它的功能正常,而該庫的方法(包括request)是從其他腳本調用的。

是什麼導致腳本卡住了?

腳本:

# 
# Album artwork downloading module for Encore Music Player application. 
# Loosely based on the producer-consumer model devised by E W Djikstra. 
# 
# The Downloader class (implemented as a daemon thread) acts as the consumer 
# in the system where it reads requests from the buffer and tries to fetch the 
# artwork from ws.audioscrobbler.com (LastFM's web service portal). 
# 
# Requester class, the producer, is a standard thread class that places the request 
# in the buffer when started. 
# 
# DBusRequester class provides an interface to the script and is made available on 
# the session bus of the DBus daemon under the name of 'com.encore.AlbumArtDownloader' 
# which enables the core music player to request downloads. 
# 

import threading, urllib, httplib2, md5, libxml2, os, dbus, dbus.service, signal 
from collections import deque 
from gi.repository import GObject 
from dbus.mainloop.glib import DBusGMainLoop 

requests = deque() 
mutex = threading.Lock() 
count = threading.Semaphore(0) 

DBusGMainLoop(set_as_default = True) 

class Downloader(threading.Thread): 

     def __init__(self): 
       threading.Thread.__init__(self) 

     def run(self): 

       while True: 
         print "=> Downloader waiting for requests" 
         count.acquire() # wait for new request if buffer is empty 

         mutex.acquire() # enter critical section 
         request = requests.popleft() 
         mutex.release() # leave critical section 

         (p, q) = request 

         try: 
           print "=> Generating api for %s by %s" % (p,q) 
           params    = urllib.urlencode({'method': 'album.getinfo', 'api_key': 'XXX', 'artist': p, 'album': q}) 
           audioscrobbler_api = "http://ws.audioscrobbler.com/2.0/?%s" % params 
           print "=> Generated URL %s" % (audioscrobbler_api) 

           http_object = httplib2.Http() 
           print "=> Requesting response" 
           resp, content = http_object.request(audioscrobbler_api) 
           print "=> Received response" 

           if not resp.status == 200: 
             print "Unable to fetch artwork for %s by %s" % (q, p) 
             continue     # proceed to the next item in queue if request fails 

           doc = libxml2.parseDoc(content) 
           ctxt = doc.xpathNewContext() 
           res = ctxt.xpathEval("//image[@size='medium']") # grab the element containing the link to a medium sized artwork 

           if len(res) < 1: 
             continue     # proceed to the next item in queue if the required image node is not found 

           image_uri = res[0].content   # extract uri from node 

           wget_status = os.system("wget %s -q --tries 3 -O temp" % (image_uri)) 

           if not wget_status == 0: 
             continue     # proceed to the next item in queue if download fails 

           artwork_name = "%s.png" % (md5.md5("%s + %s" % (p, q)).hexdigest()) 

           os.system("convert temp -resize 64x64 %s" % artwork_name) 
         except: 
           pass       # handle http request error 

class Requester(threading.Thread): 

     def __init__(self, request): 
       self.request = request 
       threading.Thread.__init__(self) 

     def run(self): 
       mutex.acquire() # enter critical section 
       if not self.request in requests: 
         requests.append(self.request) 
         count.release() # signal downloader 

         mutex.release() # leave critical section 

class DBusRequester(dbus.service.Object): 

     def __init__(self): 
       bus_name = dbus.service.BusName('com.encore.AlbumArtDownloader', bus=dbus.SessionBus()) 
       dbus.service.Object.__init__(self, bus_name, '/com/encore/AlbumArtDownloader') 

     @dbus.service.method('com.encore.AlbumArtDownloader') 
     def queue_request(self, artist_name, album_name): 

       request = (artist_name, album_name) 
       requester = Requester(request) 
       requester.start() 

def sigint_handler(signum, frame): 
     """Exit gracefully on receiving SIGINT.""" 

     loop.quit() 

signal.signal(signal.SIGINT, sigint_handler) 

downloader_daemon = Downloader() 
downloader_daemon.daemon = True 
downloader_daemon.start() 

requester_service = DBusRequester() 

loop = GObject.MainLoop() 
loop.run() 

做一個按Ctrl-C

=> Downloader waiting for requests 
=> Generating api for paul van dyk by evolution 
=> Generated URL http://ws.audioscrobbler.com/2.0/?album=evolution&api_key=XXXXXXXXXXXXXXXXXXXX&method=album.getinfo&artist=paul+van+dyk 
=> Requesting response 
^C 

謝謝!

回答

0

當你的腳本卡在53行時,你可以使用Ctrl + C打破執行並向我們顯示python traceback給出的內容嗎?

+0

我已經添加了輸出。它不會產生一個! – Utsav

0

該問題是由Python的全局解釋器鎖(GIL)引起的。

GObject.threads_init() 

解決了這個問題。