修复了strptime异常但导致程序变慢

4 投票
2 回答
1825 浏览
提问于 2025-04-16 09:34

我有一段代码,当它在一个线程里运行时(完整代码在这里 - https://github.com/eWizardII/homobabel/blob/master/lovebird.py

 for null in range(0,1):
            while True:
                try:
                    with open('C:/Twitter/tweets/user_0_' + str(self.id) + '.json', mode='w') as f:
                        f.write('[')
                        threadLock.acquire()
                        for i, seed in enumerate(Cursor(api.user_timeline,screen_name=self.ip).items(200)):
                            if i>0:
                                f.write(", ")
                            f.write("%s" % (json.dumps(dict(sc=seed.author.statuses_count))))
                            j = j + 1
                        threadLock.release()
                        f.write("]")
                except tweepy.TweepError, e:
                    with open('C:/Twitter/tweets/user_0_' + str(self.id) + '.json', mode='a') as f:
                        f.write("]")
                    print "ERROR on " + str(self.ip) + " Reason: ", e
                    with open('C:/Twitter/errors_0.txt', mode='a') as a_file:
                        new_ii = "ERROR on " + str(self.ip) + " Reason: " + str(e) + "\n"
                        a_file.write(new_ii)
                break

现在如果没有线程锁,我会遇到以下错误:

Exception in thread Thread-117: Traceback (most recent call last):   File "C:\Python27\lib\threading.py", line 530, in __bootstrap_inner
    self.run()   File "C:/Twitter/homobabel/lovebird.py", line 62, in run
    for i, seed in enumerate(Cursor(api.user_timeline,screen_name=self.ip).items(200)): File "build\bdist.win-amd64\egg\tweepy\cursor.py", line 110, in next
    self.current_page = self.page_iterator.next()   File "build\bdist.win-amd64\egg\tweepy\cursor.py", line 85, in next
    items = self.method(page=self.current_page,
*self.args, **self.kargs)   File "build\bdist.win-amd64\egg\tweepy\binder.py", line 196, in _call
    return method.execute()   File "build\bdist.win-amd64\egg\tweepy\binder.py", line 182, in execute
    result = self.api.parser.parse(self, resp.read())   File "build\bdist.win-amd64\egg\tweepy\parsers.py", line 75, in parse
    result = model.parse_list(method.api, json)   File "build\bdist.win-amd64\egg\tweepy\models.py", line 38, in parse_list
    results.append(cls.parse(api, obj))   File "build\bdist.win-amd64\egg\tweepy\models.py", line 49, in parse
    user = User.parse(api, v)   File "build\bdist.win-amd64\egg\tweepy\models.py", line 86, in parse
    setattr(user, k, parse_datetime(v))   File "build\bdist.win-amd64\egg\tweepy\utils.py", line 17, in parse_datetime
    date = datetime(*(time.strptime(string, '%a %b %d %H:%M:%S +0000 %Y')[0:6]))   File "C:\Python27\lib\_strptime.py", line 454, in _strptime_time
    return _strptime(data_string, format)[0]   File "C:\Python27\lib\_strptime.py", line 300, in _strptime
    _TimeRE_cache = TimeRE()   File "C:\Python27\lib\_strptime.py", line 188, in __init__
    self.locale_time = LocaleTime()   File "C:\Python27\lib\_strptime.py", line 77, in __init__
    raise ValueError("locale changed during initialization") ValueError: locale changed during initialization

问题在于,当线程锁开启时,每个线程基本上是一个接一个地运行,这样每次循环的运行时间就变得非常长,根本没有使用线程的好处。所以,如果没有办法去掉线程锁,有没有办法让try语句里的for循环运行得更快呢?

2 个回答

2

你遇到的问题和使用的函数和模块没有线程安全性有关。

这里可以看到,tweepy这个库既不支持重入,也不是线程安全的。再看看这里,Python的LocaleTime也是这样。

对于像你这样的多线程应用,建议你通过自己的类来封装tweepy API,并确保这个类是同步的(使用RLock)。但是不要直接从tweepy类继承,而是要和tweepy实例建立一个拥有的关系,作为一个私有属性。

6

根据之前在StackOverflow上的一个回答time.strptime这个函数在多线程环境下是不安全的。可惜的是,那篇回答提到的错误和你遇到的错误不一样。

他们的解决办法是,在启动任何线程之前先调用一次time.strptime,然后在不同的线程中再调用time.strptime就可以正常工作了。

我觉得这个方法可能在你的情况也能奏效,我查看了_strptimelocale这两个标准库模块。我不能确定这个方法一定有效,因为我无法在本地测试你的代码,但我想给你提供一个可能的解决方案。

如果这个方法有效,请告诉我。

编辑:

我做了一些额外的研究,发现Python标准库在locale.h这个C语言头文件中调用了setlocale。根据setlocale的文档,这个函数在多线程环境下也是不安全的,应该在初始化线程之前调用setlocale,就像我之前提到的那样。

不幸的是,每次你调用time.strptime时,都会调用setlocale。所以,我建议你这样做:

  1. 先试试之前提到的解决方案,尝试在初始化线程之前调用time.strptime,并去掉锁。
  2. 如果第一步不行,你可能需要自己写一个线程安全的time.strptime函数,正如Python文档中提到的locale模块。

撰写回答