【问题标题】:Threading memory profiling线程内存分析
【发布时间】:2014-09-15 13:49:54
【问题描述】:

所以我希望这不是重复的,但是我要么无法找到适当的解决方案,要么我不是 100% 了解我正在寻找的东西。我编写了一个程序来处理大量请求。我创建了一个线程来

  1. 从许多 api 获取响应,例如:share.yandex.ru/gpp.xml?url=MY_URL 以及抓取博客
  2. 使用python-goose解析上例/json/中所有请求的响应提取文章
  3. 将解析结果返回主线程并插入数据库。

这一切都进展顺利,直到它需要拉回我以前没有测试过的大量数据。造成这种情况的主要原因是它使我超过了共享 Linux 服务器 (512mb) 上的共享内存限制,从而启动了 kill。这应该足够了,因为它只有几千个请求,尽管我可能是错的。我正在清除主线程中的所有大型数据变量/对象,但这似乎也无济于事。

我在主函数上运行了一个 memory_profile,它使用如下所示的线程类创建线程:

class URLThread(Thread):
    def __init__(self,request):
        super(URLThread, self).__init__()
        self.url = request['request']
        self.post_id = request['post_id']
        self.domain_id = request['domain_id']
        self.post_data = request['post_params']
        self.type = request['type']
        self.code = ""
        self.result = ""
        self.final_results = ""
        self.error = ""
        self.encoding = ""

    def run(self):
        try:
            self.request = get_page(self.url,self.type)
            self.code = self.request['code']
            self.result = self.request['result']
            self.final_results = response_handler(dict(result=self.result,type=self.type,orig_url=self.url ))
            self.encoding = chardet.detect(self.result)
            self.error = self.request['error']
        except Exception as e:
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            errors.append((exc_type, fname, exc_tb.tb_lineno,e,'NOW()'))
            pass

@profile
def multi_get(uris,timeout=2.0):
    def alive_count(lst):
        alive = map(lambda x : 1 if x.isAlive() else 0, lst)
        return reduce(lambda a,b : a + b, alive)
    threads = [ URLThread(uri) for uri in uris ]
    for thread in threads:
        thread.start()
    while alive_count(threads) > 0 and timeout > 0.0:
        timeout = timeout - UPDATE_INTERVAL
        sleep(UPDATE_INTERVAL)
    return [ {"request":x.url,
              "code":str(x.code),
              "result":x.result,
              "post_id":str(x.post_id),
              "domain_id":str(x.domain_id),
              "final_results":x.final_results,
              "error":str(x.error),
              "encoding":str(x.encoding),
              "type":x.type}
            for x in threads ]

在我通过它的第一批请求中,结果看起来像这样(仅供参考,这是一个链接,因为此处的输出文本不可读,我无法粘贴 html 表格或嵌入图像,直到我得到还有2分):

http://tinypic.com/r/28c147d/8

并且它似乎不会在后续传递中丢弃任何内存(我一次批处理 100 个请求/线程)。我的意思是,一旦一批线程完成,它们似乎每次运行另一个线程时都会留在内存中,内存添加如下:

http://tinypic.com/r/nzkeoz/8

我在这里做了什么很愚蠢的事情吗?

【问题讨论】:

标签: python multithreading memory-management memory-leaks


【解决方案1】:

当没有对该对象的引用时,Python 通常会释放该对象占用的内存。您的multi_get 函数返回一个列表,其中包含对您创建的每个线程的引用。因此,Python 不太可能释放该内存。但我们需要查看调用multi_get 的代码在做什么才能确定。

要开始释放内存,您需要停止从该函数返回对线程的引用。或者如果你想继续这样做,至少在某处删除它们del x

【讨论】:

  • 嗯,好吧,这是有道理的。调用 multi_get 函数的代码只是一个简单的循环,通过一个包含请求和一些附加数据的列表,然后循环遍历结果并构建 mysql 插入。因此,如果我将线程对象转换为列表或其他一些更简单的对象/变量,删除线程并从 multi_get 返回可能正常工作的对象(不是线程)?
  • 是的,就可以了。尝试从 multi_get 函数返回 Python 原语(如字符串)。这可能会奏效。
猜你喜欢
  • 2014-10-27
  • 2014-06-21
  • 2016-10-02
  • 2011-06-15
  • 2010-10-21
  • 1970-01-01
  • 2016-02-10
  • 1970-01-01
相关资源
最近更新 更多