Python线程似乎无缘无故地停止/冻结/挂起?可能的原因



在我看来,线程在没有任何原因的情况下突然停止执行,并且再也不会恢复或重新启动。造成这种行为的可能原因是什么?没有引发异常。至少没有检测到或打印出异常。它只是在没有任何信息的情况下停止。我的pygtk GUI和其他一切都在继续运行,没有出现任何问题。

它仅限于一段代码,但它发生在该代码中的任何地方。以下代码在该线程内部运行。我在代码中插入了很多打印,因为我无法调试它。调试器也会冻结。在没有调试器的情况下运行不会改变它(所以这不是调试的副作用)

count = 0
while True: 
            count = count + 1
            #read results calculated by another thread. Done via Queue.Queue
            pos, phrase, site, extractor, infoextractor, image = self.htmlqueue.get(True)
            print """, threading.currentThread(), """, site, image["link"], infoextractor.__class__.__name__ , FileDownloader.nbdownloads, count
            print "1"
            #if Nones are found in the queue it means that there will be no more data
            if pos is None and phrase is None and site is None and extractor is None and infoextractor is None and image is None: break
            print "2"
            if printstuff: print "preDownloadAll1", image["link"],
            print "3"
            try:
                info = infoextractor.extractValues()
                print info
            except (object) as e:
                print "exception!"
            print "5"    
            if info is None: 
                print "_5.1_"
                continue
            print "6"
            if len(info) == 0: 
                print "_6.1_"
                continue
            print "7"
            if "google" in site:
                print "8"
                adr = image["smallthumb"]
                filename = ImageManager.extractFileFromURL(image["smallthumb"])
            elif info.has_key("thumb"):
                print "9"
                adr = info["thumb"]
                filename = ImageManager.extractFileFromURL(info["thumb"])
            else:
                print "10"
                adr = image["thumb"]
                filename = ImageManager.extractFileFromURL(image["thumb"])
            print "11" 
            localfile = self.imagelocations[site] + "/" + filename
            print "12"
            t = None
            if (not os.path.isfile(localfile)) and predownloadjpegs:
                print "13"
                t = FileDownloader.downloadFileNewThread(url = adr, localtargetdir = self.imagelocations[site], timetofinish = 100)
            print "14"
            tds.append((t, pos, phrase, site, extractor, infoextractor, image, info, adr, localfile))
            print "15"
            if count%100 == 0: print count, "n"
            print "16"
#            seen[image["link"]] = True
        print "17"

代码平均运行3000-5000次计数,并在不同的队列条目处停止(大部分html都被缓存;)。挂起之前的最后一个输出是随机的(每次重新启动应用程序时都不同)。大多数时候是3,有时是16。数字17永远也达不到。我也得了7分。还有一次它打印了信息,但没有再打印5。还有一次,它打印了一半的信息字符串。

由于大部分时间都是3点,我怀疑那里有异常,可能还有一些非常奇怪的延迟检测。但是没有!在我的测试过程中从未执行打印"exception!"。

我的线程在冻结后仍保留在内存中,它并没有阻止

self.htmlqueue.get(True)

因为如果我做

pos, phrase, site, extractor, infoextractor, image = None, None, None, None, None, None
                success = False
                while not success:
                    try:
                        pos, phrase, site, extractor, infoextractor, image = self.htmlqueue.get(True,10)
                        success = True
                    except:
                        print "no success", count
                        success = False

然后冻结继续,在大多数运行中根本没有"不成功"的输出。更糟糕的是,我使用信号量一次运行一个代码段,这样所有其他线程都会被阻塞,等待这一个线程完成。

主GUI线程继续运行而不会受到干扰。所有我自己的线程都应该在执行后死亡(我用mythread.setDaemon(True)解除了它们的名字)。我的python版本是2.7.3

我也在考虑一个输出缓冲区的可能性,它会使它看起来只是随机的(事实上,它总是在同一个地方,但一些输出可能仍然挂在输出缓冲区中)。但由于每次打印都会引入一行新行,我猜每个输出都会立即刷新,所以在线程冻结时我不会错过任何输出。我的开发环境是使用pydev的eclipse。

FileDownloader.downloadFileNewThread从该线程内部启动另一个线程。这也可能是个问题吗?线程启动其他线程?如果我不妖魔化和妖魔化似乎也没有什么区别。

在我看来,代码确实是随机冻结的。但为什么会这样,怎么会这样???

好吧,伙计们,在我的头撞到墙上三天后,我想我解决了这个问题。至少什么都没有了。

Pygtk似乎是原因http://faq.pygtk.org/index.py?file=faq20.006.htp&req=显示

Pygtk能够永远阻止长时间运行的线程。它获得了GIL锁并且不放手。线程只是挂在内存中,但不再执行。

我所要做的就是打电话给

gobject.threads_init()

在其他任何与gtk相关的事情之前。换句话说,我的代码开始时是这样的

import gtk
import gobject
import MainGUI
if __name__ == "__main__":
    gobject.threads_init()
    from MainGUI import MainGUI
    MainGUI.instance = MainGUI()
    gtk.main()

幸运的是,我严格地将逻辑与GUI分离,并且从不在自己的线程中实例化任何gtk元素。所以这是我唯一真正需要的台词。如果有人仍然需要能够在自己的线程中处理GUI对象,他必须使用

gobject.idle_add(callback_function, args)

我学到的另一个教训是,队列的内存容量可能非常有限,如果它们被塞满太多,也会导致阻塞行为。

启动另一个线程应该不会有任何问题,因为据我所知,这些线程都是"在同一级别"启动的,python不知道或维护它们之间的任何层次结构。这只是pygtk搞砸的关于GIL(全局解释器锁)的伪随机斗争。

最新更新