通过ctypes在多线程中调用DLL是否可行?启动第二线程时脚本异常停止原因是什么
问题原因
首先明确:ctypes本身支持多线程调用DLL函数,你遇到的崩溃和退出是代码实现错误导致的,核心原因有3个:
- DllMain中调用了非线程安全的IO函数:Windows执行DllMain时会持有全局加载器锁,
printf这类CRT函数内部自带IO锁,当第二个线程启动触发DLL_THREAD_ATTACH通知时,两个锁发生冲突直接触发死锁或程序崩溃。 - 重复加载DLL:你将
ctypes.CDLL的加载逻辑写在了死循环内,每次循环都会重复调用系统API加载DLL,虽然系统只会增加DLL引用计数,但并发加载操作会放大DllMain的锁冲突概率。 - 守护线程被强制终止:你创建的两个线程都设置了
daemon=True,主线程在启动t2、打印完still here...后就会直接退出,所有守护线程会被操作系统强制杀死,表现为程序突然停止。
另外你DLL中test函数的静态变量a没有加同步保护,会存在计数错乱的竞态问题,但不会直接导致程序崩溃。
修复方案
1. 修改DLL代码
移除DllMain内所有非必要操作,给test函数加线程同步:
// ctypes-example.hpp #pragma once #include <windows.h> #include <stdio.h> extern "C" { void init(void); void cleanup(void); void test(void); } // ctypes-example.cpp #include "ctypes-example.hpp" static CRITICAL_SECTION cs; static int a = 0; // 单独抽离初始化逻辑,不要在DllMain里执行复杂操作 extern "C" void init() { InitializeCriticalSection(&cs); printf("Load working...\n"); } extern "C" void cleanup() { DeleteCriticalSection(&cs); printf("Unload working...\n"); } void test(void) { EnterCriticalSection(&cs); printf("Hello from dll: %i\n", a); ++a; LeaveCriticalSection(&cs); } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { return TRUE; // DllMain仅返回成功即可,不执行其他逻辑 }
2. 修改Python代码
全局只加载一次DLL,添加线程等待逻辑:
import ctypes import threading import time # 全局仅加载一次DLL my = ctypes.CDLL('/cygdrive/m/Workspace/py/src/cpp/ctypes-example.dll') my.init.restype = None my.init.argtypes = () my.test.restype = None my.test.argtypes = () my.cleanup.restype = None my.cleanup.argtypes = () # 初始化DLL资源 my.init() def tfunc(): while True: my.test() # 可加小幅休眠避免占满CPU,按需调整 time.sleep(0.001) t1 = threading.Thread(target=tfunc) t2 = threading.Thread(target=tfunc) t1.start() time.sleep(1) t2.start() print('still here...') # 等待线程执行,如需停止可按Ctrl+C终止 try: t1.join() t2.join() finally: my.cleanup()
3. 编译命令保持原有逻辑即可
修改后程序即可正常在多线程下调用DLL函数,不会出现突然停止的问题。
内容的提问来源于stack exchange,提问作者Adam
相关产品推荐
相关产品推荐

