C中的Python线程

8 投票
2 回答
2492 浏览
提问于 2025-04-16 20:56

我正在用C语言写一个多线程程序。在创建线程之前,我通过调用 Py_Initialize() 来初始化一个全局的Python环境。然后,在每个创建的线程中,这个全局的Python环境是共享的,每个线程都会调用一个Python方法,并将参数从C语言转换过去。到这里一切都很顺利。

但是,当我在加载的Python模块中使用 time.sleep() 时,C程序就会出现 Segmentation Fault(段错误)。而且,这个加载的Python模块还需要加载另一个C库来继续工作。我写了一个简单的计数器库来测试这个问题:

# python part, call the counter function
lib = ctypes.cdll.LoadLibrary(libpycount.so)
for i in xrange(10):
    lib.count()
// C part, dummy countings
#include <stdio.h>
int counter = 1;
void
count() {
    printf("counter:%d \n", counter);
    counter++;
}

我猜这可能是因为我没有正确管理复杂的线程创建。我在Python文档中发现了 非Python创建的线程 的相关内容。

有没有什么想法或建议呢?

2 个回答

2

问题是Python解释器是否线程安全——这是文档中关于在同一个进程空间中运行多个解释器的说明:

错误和注意事项:因为子解释器(和主解释器)都是同一个进程的一部分,所以它们之间的隔离并不完美。例如,使用像os.close()这样的低级文件操作时,它们可能会(无意中或恶意地)影响彼此打开的文件。由于扩展在(子)解释器之间是共享的,有些扩展可能无法正常工作;尤其是当扩展使用了(静态)全局变量,或者在初始化后修改其模块的字典时。这意味着可以将一个子解释器中创建的对象插入到另一个子解释器的命名空间中;这样做时要非常小心,以避免在子解释器之间共享用户定义的函数、方法、实例或类,因为这些对象执行的导入操作可能会影响错误的(子)解释器的已加载模块字典。(XXX 这是一个难以修复的错误,将在未来的版本中解决。)

...而且我认为Python线程和C/C++中的原生线程并不是同一回事。

4

我的问题已经解决了。你们可能有更具体的问题,所以我在这里尝试用更通用的方式写出我的解决方案。希望能对你们有所帮助。


- 在主C线程中

  • 在一开始就初始化Python环境:
/*define a global variable to store the main python thread state*/
PyThreadState * mainThreadState = NULL;

if(!Py_IsInitialized())
    Py_Initialize();

mainThreadState = = PyThreadState_Get();
  • 然后启动C线程:
pthread_create(pthread_id, NULL, thread_entrance, NULL);



- 在每个线程中,或者说在thread_entrance函数的主体中

  • 准备环境:
/*get the lock and create new python thread state*/
PyEval_AcquireLock();
PyInterpreterState * mainInterpreterState = mainThreadState->interp;
PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState);
PyEval_ReleaseLock();    /*don't forget to release the lock*/

/*
 * some C manipulations here
 */
  • 在这里放入嵌入的Python代码:
/*get the lock and put your C-Python code here*/
PyEval_AcquireLock();
PyThreadState_Swap(myThreadState);    /*swap your python thread state*/

PyEval_CallObject(py_function, py_arguments);
/*or just something like PyRun_SimpleString("print \"hello world\""); for test*/

PyThreadState_Swap(NULL);    /*clean the thread state before leaving*/
PyEval_ReleaseLock();



- 回到主C线程

  • 当每个线程完成工作后,结束Python环境
pthread_join(pthread_id, NULL);
PyEval_RestoreThread(mainThreadState);
Py_Finalize();

撰写回答