COM経由でいくつかのアプリケーションにアクセスする必要があるCherryPyを使用してWebアプリに取り組んでいます。
現在、リクエストごとにアプリケーションの新しいインスタンスを作成しています。つまり、各リクエストは、アプリケーションが開始するまで3秒、実際のジョブが0.01秒待機します。
各COMアプリケーションを一度起動し、存続させて、次のリクエストで数秒間再利用したいと思います。ほとんどの場合、5〜10個のajaxリクエストのバーストで使用され、その後何時間も使用されないためです。
CherryPyアプリケーションのすべてのスレッド間でCOMオブジェクトを共有することは可能ですか?
これは、各リクエストで現在どのように機能しているか、スレッド間でどのように機能しないかを示すいくつかの実験の要約です。
次のコードは、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()
_
ただし、次のコードはExcelを起動し、3秒後に閉じます。
_>>> 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()
_
CoInitialize()
への呼び出しを追加しました。そうしないと、xl
オブジェクトが機能しません( この投稿 を参照)。
そして、3秒間の一時停止を追加したので、タスクマネージャーで、Excel.EXEプロセスが開始され、3秒間有効であることがわかりました。
それを開始したスレッドが終了した後、なぜそれが死ぬのですか?
CoInitialize()
のドキュメントを確認しましたが、マルチスレッド環境で動作させることができるかどうかわかりませんでした。
複数のスレッドでwin32comを使用する場合は、COMObject
をスレッドに直接渡すことができないため、もう少し作業を行う必要があります。スレッド間でインスタンスを渡すには、CoMarshalInterThreadInterfaceInStream()
とCoGetInterfaceAndReleaseStream()
を使用する必要があります。
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()
詳細については、以下を参照してください: https://mail.python.org/pipermail/python-win32/2008-June/007788.html