2014-11-05 14 views
9

Ich arbeite an einer Web-App mit CherryPy, die über COM auf einige Anwendungen zugreifen muss.Win32com mit Multithreading verwenden

Im Moment erstelle ich eine neue Instanz der Anwendung mit jeder Anfrage, was bedeutet, dass jede Anfrage 3 Sekunden auf den Start der Anwendung und 0.01 auf den eigentlichen Job wartet.

Ich möchte jede COM-Anwendung einmal starten und sie am Leben erhalten und es für ein paar Sekunden auf den folgenden Anforderungen wiederverwenden, weil es meistens von einem Ausbruch von 5-10 Ajax-Anfragen verwendet wird, dann nichts für Stunden .

Ist es möglich, ein COM-Abject über alle Threads einer CherryPy-Anwendung zu teilen?

Hier ist die Zusammenfassung einiger Experimente, die zeigen, wie es jetzt bei jeder Anfrage funktioniert und wie es nicht über Threads funktioniert.

Der folgende Code erfolgreich startet und stoppt Excel:

>>> import pythoncom, win32com.client 
>>> def start(): 
    global xl 
    xl = win32com.client.Dispatch('Excel.Application') 

>>> def stop(): 
    global xl 
    xl.quit() 
    xl = None 

>>> start() 
>>> stop() 

Aber der folgende Code startet Excel und schließt es nach 3 Sekunden.

>>> import pythoncom, win32com.client, threading, time 
>>> def start(): 
    global xl 
    pythoncom.CoInitialize() 
    xl = win32com.client.Dispatch('Excel.Application') 
    time.sleep(3) 

>>> threading.Thread(target=start).start() 

Ich habe den Anruf zu CoInitialize() sonst das xl Objekt würde (siehe this post) nicht funktionieren.

Und ich habe die 3 Sekunden Pause hinzugefügt, so dass ich auf dem Task-Manager sehen konnte, dass der EXCEL.EXE-Prozess startet und für 3 Sekunden am Leben ist.

Warum stirbt es, nachdem der Thread, der es begann, endet?

Ich überprüfte die Dokumentation von CoInitialize(), aber ich konnte nicht verstehen, wenn es möglich ist, es in Multithread-Umgebung zu arbeiten.

+1

Der Trick ist, den Faden für Multithread-Apartment (aka, freethreaded) verwenden zu initialisieren. Versuchen Sie [CoInitializeEx] (http://docs.activestate.com/activepython/2.5/pywin32/pythoncom__CoInitializeEx_meth.html) mit der Option "COINIT_APARTMENTTHREADED". – tdelaney

Antwort

7

Wenn Sie win32com in mehreren Threads verwenden möchten, müssen Sie ein wenig mehr Arbeit erledigen, da COMObject nicht direkt an einen Thread übergeben werden kann. Sie müssen CoMarshalInterThreadInterfaceInStream() und CoGetInterfaceAndReleaseStream() verwenden Instanz zwischen Threads zu übergeben:

import pythoncom, win32com.client, threading, time 

def start(): 
    # Initialize 
    pythoncom.CoInitialize() 

    # Get instance 
    xl = win32com.client.Dispatch('Excel.Application') 

    # Create id 
    xl_id = pythoncom.CoMarshalInterThreadInterfaceInStream(pythoncom.IID_IDispatch, xl) 

    # Pass the id to the new thread 
    thread = threading.Thread(target=run_in_thread, kwargs={'xl_id': xl_id}) 
    thread.start() 

    # Wait for child to finish 
    thread.join() 

def run_in_thread(xl_id): 
    # Initialize 
    pythoncom.CoInitialize() 

    # Get instance from the id 
    xl = win32com.client.Dispatch(
      pythoncom.CoGetInterfaceAndReleaseStream(xl_id, pythoncom.IID_IDispatch) 
    ) 
    time.sleep(5) 


if __name__ == '__main__': 
    start() 

Für weitere Informationen siehe: https://mail.python.org/pipermail/python-win32/2008-June/007788.html

+1

danke für das Code-Snippet. Es war sehr nützlich. – Sanjit