Python C API 并发问题

5 投票
3 回答
717 浏览
提问于 2025-04-17 12:30

我们正在开发一个小型的C语言服务器应用程序。这个服务器应用程序负责处理一些数据,并将结果返回给客户端。为了让数据处理部分更加灵活和可配置,我们决定使用脚本,并根据现有的各种模块选择了Python。我们使用Python-C API来在C和Python之间发送和接收数据。

算法大致是这样的:

  1. 服务器从客户端接收一些数据,这些数据会存储在C语言中创建的一个字典里。这个字典是通过API函数PyDict_New()创建的。输入数据以键值对的形式存储在字典中,使用API函数PyDict_SetItemString()。
  2. 接下来,我们执行Python脚本,使用PyRun_SimpleString()函数,并将脚本作为参数传入。这个脚本会使用在C中创建的字典。请注意,我们通过PyImport_AddModule()和PyModule_AddObject()方法让脚本可以访问这个字典。
  3. 我们将脚本处理后的结果以键值对的形式存储在上面创建的同一个字典中。C语言代码在脚本执行完后,可以简单地访问这个结果变量(键值对)。

问题

我们面临的问题是,当来自不同客户端的并发请求同时到达时,会出现对象引用计数异常。请注意,对于每个用户的请求,我们都会为该用户单独创建一个独立的字典。为了克服这个问题,我们将对PyRun_SimpleString()的调用放在了PyEval_AcquireLock()和PyEval_ReleaseLock()之间,但这样做导致脚本执行变成了一个阻塞调用。如果某个脚本执行时间较长,所有其他用户也会在等待响应。

请问您能否建议最佳的解决方案,或者指出我们哪里出错了?如需更多信息,请随时联系我。

任何帮助或指导都将不胜感激。

3 个回答

1

我建议你了解一下 multiprocessing 这个模块。

1

你可能需要看看这个链接:http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock,里面的第一段就解释了你的问题。

当你获取全局解释器锁(GIL)的时候,要在你直接操作Python对象的代码周围进行。调用PyRun_SimpleString时,它会自动处理GIL,并在长时间运行的操作或每执行X条指令时释放它。不过,这样做并不会真正实现多线程。

补充:

你需要获取这个锁,并确保Python知道它处于不同的线程状态:

// acquire the lock and switch thread state
PyEval_AcquireLock();
PyThreadState_Swap(perThreadState);

// execute some python code
PyEval_SimpleString("print 123");

// clear the thread state and release the lock
PyThreadState_Swap(NULL);
PyEval_ReleaseLock();
1

也许你漏掉了在这个回答中提到的某个调用。

撰写回答