3

Python TCP クライアントがあり、メディア (.mpg) ファイルをループで「C」TCP サーバーに送信する必要があります。

次のコードがあります。別のスレッドでファイルの10Kブロックを読み取って送信し、ループでもう一度実行しています。これは、スレッドモジュールまたはtcp sendの実装が原因だと思います。Queues を使用して 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

その部分にコメントすると問題が解決するため、キューの間違った実装が原因であることがわかりました

UPDATE 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
4

4 に答える 4

5

printlog は tkinter テキスト コントロールに追加されるため、そのコントロールが占有するメモリはメッセージごとに増加します (ログ メッセージを表示するには、すべてのログ メッセージを保存する必要があります)。

すべてのログを保存することが重要でない限り、一般的な解決策は、表示されるログ行の最大数を制限することです。

単純な実装では、コントロールがメッセージの最大数に達した後、最初から余分な行を削除します。コントロールの行数を取得する関数を追加してから、printlog に次のようなものを追加します。

while getnumlines(self.edit) > self.maxloglines:
    self.edit.delete('1.0', '1.end')

(上記のコードはテストされていません)

更新: いくつかの一般的なガイドライン

メモリ リークのように見える場合でも、必ずしも関数がwrongであること、またはメモリにアクセスできなくなっていることを意味するわけではないことに注意してください。多くの場合、要素を蓄積しているコンテナーのクリーンアップ コードが欠落しています。

この種の問題に対する基本的な一般的なアプローチは次のとおりです。

  • コードのどの部分が問題を引き起こしている可能性があるかについての意見を形成する
  • そのコードをコメントアウトしてチェックしてください(または、候補が見つかるまでコードをコメントし続けます)
  • 責任のあるコードでコンテナを探し、そのサイズを出力するコードを追加します
  • そのコンテナから安全に削除できる要素と、いつ削除するかを決定します
  • 結果をテストする
于 2013-10-18T15:30:43.163 に答える
2

Out of memory errors are indicative of data being generated but not consumed or released. Looking through your code I would guess these two areas:

  • Messages are being pushed onto a Queue.Queue() instance in the pushlog method. Are they being consumed?
  • The MainGui printlog method may be writing text somewhere. eg. Is it continually writing to some kind of GUI widget without any pruning of messages?

From the code you've posted, here's what I would try:

  1. Put a print statement in updatelog. If this is not being continually called for some reason such as a failed after() call, then the queuelog will continue to grow without bound.
  2. If updatelog is continually being called, then turn your focus to printlog. Comment the contents of this function to see if out of memory errors still occur. If they don't, then something in printlog may be holding on to the logged data, you'll need to dig deeper to find out what.

Apart from this, the code could be cleaned up a bit. self.queuelog is not created until after the thread is started which gives rise to a race condition where the thread may try to write into the queue before it has been created. Creation of queuelog should be moved to somewhere before the thread is started.

updatelog could also be refactored to remove redundancy:

def updatelog(self):
       try:
           while True:
               msg = self.queuelog.get_nowait() 
               self.printlog(msg)
        except Queue.Empty:
           pass

And I assume the the kill function is called from the GUI thread. To avoid thread race conditions, the self.over should be a thread safe variable such as a threading.Event object.

def __init__(...):
    self.over = threading.Event()

def kill(self):
    self.over.set()
于 2013-10-17T11:32:35.177 に答える