python - 是否可以使用来自多个线程的 ctypes 调用 dll?
问题描述
我有一个简单的 python 脚本,它加载一个 dll 并从两个线程调用导出的函数。看起来当第二个线程启动时脚本突然停止。这是为什么?是否不支持从多个线程调用 dll 函数?
我的脚本:
import ctypes
import threading
import time
def tfunc():
while True:
my = ctypes.CDLL('/cygdrive/m/Workspace/py/src/cpp/ctypes-example.dll')
my.test.restype = None
my.test.argtypes = ()
my.test()
t1 = threading.Thread(target=tfunc, daemon=True)
t2 = threading.Thread(target=tfunc, daemon=True)
t1.start()
time.sleep(1)
t2.start()
print('still here...')
输出:
$ python python-dll-thread.py
Load working...
Hello from dll: 0
Hello from dll: 1
...
Hello from dll: 1078
$
ctypes-example.hpp:
#pragma once
#include <windows.h>
extern "C" {
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);
void test(void);
}
ctypes-example.cpp:
#include <stdio.h>
#include "ctypes-example.hpp"
void test(void) {
static int a = 0;
printf("Hello from dll: %i\n", a);
++a;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
// Code to run when the DLL is loaded
printf ("Load working...\n");
break;
case DLL_PROCESS_DETACH:
// Code to run when the DLL is freed
printf ("Unload working...\n");
break;
case DLL_THREAD_ATTACH:
// Code to run when a thread is created during the DLL's lifetime
printf ("ThreadLoad working...\n");
break;
case DLL_THREAD_DETACH:
// Code to run when a thread ends normally.
printf ("ThreadUnload working...\n");
break;
}
return TRUE;
}
这是我在 cygwin 上构建这个 dll 的方法:
$ g++ -Wall -Wextra -pedantic -c -fPIC ctypes-example.cpp -o ctypes-example.o
$ gcc -shared ctypes-example.o -o ctypes-example.dll
解决方案
一个问题是两个线程都是守护进程,当唯一的非守护线程退出时,守护线程终止。
由于您正在编写一个DllMain
,您可能还想阅读动态链接库最佳实践。该代码在 Microsoft 编译器上适用于我,但如果其他运行时触发其他 DllMain 调用,它们可能会在 DllMain 中创建竞争条件(可能是由于 printf?)。
这适用于 Microsoft 编译器:
测试.c
#include <windows.h>
#include <stdio.h>
#define API __declspec(dllexport)
API void test(void) {
static int a = 0;
printf("Hello from dll: %i\n", a);
++a;
}
API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
// Code to run when the DLL is loaded
printf ("Load working...\n");
break;
case DLL_PROCESS_DETACH:
// Code to run when the DLL is freed
printf ("Unload working...\n");
break;
case DLL_THREAD_ATTACH:
// Code to run when a thread is created during the DLL's lifetime
printf ("ThreadLoad working...\n");
break;
case DLL_THREAD_DETACH:
// Code to run when a thread ends normally.
printf ("ThreadUnload working...\n");
break;
}
return TRUE;
}
测试.py
import ctypes
import threading
import time
my = ctypes.CDLL('./test')
my.test.restype = None
my.test.argtypes = ()
def tfunc():
while True:
my.test()
time.sleep(.1) # slow down a bit
t1 = threading.Thread(target=tfunc, daemon=True)
t2 = threading.Thread(target=tfunc, daemon=True)
t1.start()
t2.start()
print('still here...')
time.sleep(1) # illustrate threads stop after main thread exits
输出
Load working...
ThreadLoad working...
Hello from dll: 0
ThreadLoad working...
Hello from dll: 1
still here...
Hello from dll: 2
Hello from dll: 2
Hello from dll: 4
Hello from dll: 4
Hello from dll: 6
Hello from dll: 6
Hello from dll: 8
Hello from dll: 8
Hello from dll: 10
Hello from dll: 10
Hello from dll: 12
Hello from dll: 12
Hello from dll: 14
Hello from dll: 14
Hello from dll: 16
Hello from dll: 16
Hello from dll: 18
Hello from dll: 18
Unload working...
推荐阅读
- git - 如何使 git 冲突文件以所需的方式排序以解决
- sql-server - SSIS OLE DB 目标 - 错误输出重定向错误,没有说明
- html - 如何在 VB.Net 中从 Body 中获取所有元素,例如
? - java - 通过工厂bean覆盖spring数据源属性
- angular - 当我尝试将 2 个 map() RxJS 运算符链接到 pipe() 时,为什么会出现此错误?
- typo3 - TYPO3 9.5 后端服务器时间错误
- c# - 将 StringContent Json 转换为 byte[]
- node.js - 如何使用 lambda 函数(nodejs)将 json 数据逐行导入 dynamodb
- solr - Solr如何将搜索范围缩小到特定品牌并在单个查询中获取该品牌的所有兄弟姐妹?
- ios - 使用空参数调用 IntentHandler