使用win32com进行多线程。

20

我正在使用CherryPy开发一个Web应用程序,需要通过COM访问几个应用程序。

目前,我每次请求都会创建一个新的应用程序实例,这意味着每个请求需要等待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对象将无法工作(请参见此帖子)。

并且我添加了三秒钟的暂停时间,这样我就可以在任务管理器上看到EXCEL.EXE进程启动并保持运行状态三秒钟。

为什么它会在启动它的线程结束后死亡呢?

我查阅了CoInitialize()的文档,但我无法理解它是否可以在多线程环境中工作。


1
可能的诀窍是为多线程公寓(又称自由线程)使用初始化线程。尝试使用COINIT_APARTMENTTHREADED选项的CoInitializeEx - tdelaney
3个回答

37

如果您想在多个线程中使用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


非常感谢!我想知道为什么如果线程中有循环,其他线程会等待。我添加了一个“line”参数,并期望每n行中的同一列单元格同时更改。 - SDIdo

1

@Mauriusz Jamro(https://dev59.com/8F8d5IYBdhLWcg3weyM0#27966218)的答案非常有帮助。另外,请确保执行以下操作:

pythoncom.CoUninitialize ()

最后,确保没有内存泄漏。您可以在使用CoInitialize()之后,在进程结束之前的某个地方调用它。


你对评论特权的了解已经到了能够用言语表达的地步,但你并没有这个特权。你知道规则 https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead 。在这种情况下,请不要决定滥用其他机制(回答)来做一些它本来不应该做的事情,而且你还没有被允许这样做。 - Yunnosch
在现有答案的基础上进行补充似乎对我来说没问题... - Yunnosch

-3

尝试使用多进程。经过长时间的搜索,这对我很有效。

from multiprocessing import Process

p = Process(target=test, args=())
p.start()
p.join()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接