2014-11-05 29 views
9

Pracuję nad aplikacją internetową z CherryPy, która potrzebuje dostępu do kilku aplikacji za pośrednictwem COM.Używanie win32com z wielowątkowością

W tej chwili tworzę nowe wystąpienie aplikacji dla każdego żądania, co oznacza, że ​​każde żądanie czeka 3 sekundy na uruchomienie aplikacji i 0,01 dla rzeczywistej pracy.

Chciałbym uruchomić każdą aplikację COM jeden raz i zachować ją przy życiu i ponownie używać przez kilka sekund w następujących żądaniach, ponieważ przez większość czasu jest używana przez serię 5-10 zapytań ajax, a następnie nic przez wiele godzin .

Czy można udostępniać abuzję COM we wszystkich wątkach aplikacji CherryPy?

Oto podsumowanie kilku eksperymentów pokazujących, jak działa teraz każde żądanie i jak nie działa w wątkach.

Poniższy kod z powodzeniem uruchamia i zatrzymuje 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() 

ale następujący kod rozpoczyna Excel i zamyka go po 3 sekundach.

>>> 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() 

dodałem wywołanie CoInitialize() inaczej przedmiot xl nie będzie działać (patrz this post).

Dodałem 3-sekundową przerwę, dzięki czemu mogłem zobaczyć na menedżerze zadań, że rozpoczyna się proces EXCEL.EXE i jest on aktywny przez 3 sekundy.

Dlaczego umiera po zakończeniu wątku, który ją rozpoczął?

Sprawdziłem dokumentację CoInitialize(), ale nie mogłem zrozumieć, czy można go uruchomić w środowisku wielowątkowym.

+1

Sztuką może zainicjować wątek dla wielowątkowym apartamentu (aka, free-gwintowany) używać. Wypróbuj [CoInitializeEx] (http://docs.activestate.com/activepython/2.5/pywin32/pythoncom__CoInitializeEx_meth.html) za pomocą opcji 'COINIT_APARTMENTTHREADED'. – tdelaney

Odpowiedz

7

Jeśli chcesz używać Win32com w wielu wątkach, musisz zrobić trochę więcej pracy, ponieważ COMObject nie można bezpośrednio przekazać do wątku. Trzeba użyć CoMarshalInterThreadInterfaceInStream() i CoGetInterfaceAndReleaseStream() przekazać instancję między wątków:

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() 

Aby uzyskać więcej informacji zobacz: https://mail.python.org/pipermail/python-win32/2008-June/007788.html

+1

dzięki za fragment kodu. to było całkiem przydatne. – Sanjit