Python 在线程内调用时排队内存泄漏

Dev*_*evC 3 python queue multithreading memory-leaks python-2.7

我有 python TCP 客户端,需要循环发送媒体(.mpg)文件到“C”TCP 服务器。

我有以下代码,在单独的线程中,我正在读取 10K 文件块并发送它,然后在循环中再次执行此操作,我认为这是因为我实现了线程模块或 tcp 发送。我正在使用队列在 GUI ( Tkinter ) 上打印日志,但一段时间后它会耗尽内存。

更新 1 - 根据要求添加更多代码

线程类“Sendmpgthread”用于创建发送数据的线程

.
. 
def __init__ ( self, otherparams,MainGUI):
    .
    .
    self.MainGUI = MainGUI
    self.lock = threading.Lock()
    Thread.__init__(self)

#This is the one causing leak, this is called inside loop
def pushlog(self,msg):
    self.MainGUI.queuelog.put(msg)

def send(self, mysocket, block):
    size = len(block)
    pos = 0;
    while size > 0:
        try:
            curpos = mysocket.send(block[pos:])
        except socket.timeout, msg:
            if self.over:
                 self.pushlog(Exit Send)
                return False
        except socket.error, msg:
            print 'Exception'     
            return False  
        pos = pos + curpos
        size = size - curpos
    return True

def run(self):
    media_file = None
    mysocket = None 

    try:
        mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        mysocket.connect((self.ip, string.atoi(self.port)))
        media_file = open(self.file, 'rb') 

        while not self.over:
            chunk = media_file.read(10000)
            if not chunk:   # EOF Reset it
                print 'resetting stream'
                media_file.seek(0, 0)
                continue
            if not self.send(mysocket, chunk): # If some error or thread is killed 
                break;

            #disabling this solves the issue
            self.pushlog('print how much data sent')       

    except socket.error, msg:
        print 'print exception'
    except Exception, msg:
        print 'print exception'

    try:
        if media_file is not None:
            media_file.close()
            media_file = None            
        if mysocket is not None:
            mysocket.close()
            mysocket = None
    finally:
            print 'some cleaning'   

def kill(self):
    self.over = True
Run Code Online (Sandbox Code Playgroud)

我发现这是因为 Queue 的错误实现,因为评论该部分解决了问题

更新 2 - 从上面的 Thread 类调用的 MainGUI 类

class MainGUI(Frame):
    def __init__(self, other args):
       #some code
       .
       .
        #from the above thread class used to send data
        self.send_mpg_status = Sendmpgthread(params)
        self.send_mpg_status.start()     
        self.after(100, self.updatelog)
        self.queuelog = Queue.Queue()

    def updatelog(self):
       try:
           msg = self.queuelog.get_nowait() 

           while msg is not None:
               self.printlog(msg)
               msg = self.queuelog.get_nowait() 
        except Queue.Empty:
           pass

        if self.send_mpg_status: # only continue when sending   
            self.after(100, self.updatelog)

    def printlog(self,msg):
        #print in GUI
Run Code Online (Sandbox Code Playgroud)

Mih*_*tan 5

由于 printlog 添加到 tkinter 文本控件中,因此该控件占用的内存将随着每条消息而增长(它必须存储所有日志消息才能显示它们)。

除非存储所有日志至关重要,否则常见的解决方案是限制显示的日志行的最大数量。

一个简单的实现是在控件达到最大消息数后从一开始就消除多余的行。添加一个函数来获取控件中的行数,然后在 printlog 中添加类似以下内容的内容:

while getnumlines(self.edit) > self.maxloglines:
    self.edit.delete('1.0', '1.end')
Run Code Online (Sandbox Code Playgroud)

(以上代码未经测试)

更新:一些一般准则

请记住,看起来像内存泄漏的情况并不总是意味着函数是内存泄漏wrong,或者内存不再可访问。很多时候,正在积累元素的容器缺少清理代码。

解决此类问题的基本通用方法:

  • 对代码的哪一部分可能导致问题形成意见
  • 通过注释掉该代码来检查它(或继续注释代码,直到找到候选者)
  • 在负责的代码中查找容器,添加代码以打印其大小
  • 决定可以从该容器中安全地删除哪些元素以及何时执行此操作
  • 测试结果