从C并行调用python

从C并行调用python,python,c,parallel-processing,openmp,python-c-api,Python,C,Parallel Processing,Openmp,Python C Api,我需要从我的C代码中调用Python函数。 它工作得很好,但当我想进行并行化时,它就崩溃了。 请参阅以下最小C代码: #include <Python.h> #include <stdio.h> int main(void) { double Z = 1.; double k = 1.; double l = 1.; double eta = -Z/k; Py_Initialize(); PyObject* pName = PyString_

我需要从我的C代码中调用Python函数。 它工作得很好,但当我想进行并行化时,它就崩溃了。 请参阅以下最小C代码:

#include <Python.h>
#include <stdio.h>

int main(void)
{
  double Z = 1.;
  double k = 1.;
  double l = 1.;
  double eta = -Z/k;

  Py_Initialize();

  PyObject* pName = PyString_FromString("mpmath");
  PyObject* pModule = PyImport_Import(pName);
  PyObject* pFunc = PyObject_GetAttrString(pModule, "coulombf");

  PyObject* pl = PyFloat_FromDouble(l);
  PyObject* peta = PyFloat_FromDouble(eta);

  int i;
#pragma omp parallel for private(i)
  for(i=0; i<10000; i++)
  {
    double r = 0.01*i;
    PyObject* prho = PyFloat_FromDouble(k*r);
    PyObject* pArgs = PyTuple_Pack(3, pl, peta, prho);
    PyObject* pValue = PyObject_CallObject(pFunc, pArgs);
    double value = PyFloat_AsDouble(pValue);
    printf("r=%.2f\tf=%.6f\n",r,value);
  }

  Py_Finalize();
  return 0;
}

但是在C语言中我该怎么做呢?我还没有找到方法。

在python中,任何类型的真正多线程(例如,在一个进程中使用多个系统线程)都是不可能的,至少在最常见的python实现中是不可能的。您既可以不使用任何类型的并行化,也可以切换到没有GIL的实现。这里有一篇文章提供了关于这个主题的更多信息:

python中任何类型的真正多线程(例如,在一个进程中使用多个系统线程)都是不可能的,至少在最常见的python实现中是不可能的。您既可以不使用任何类型的并行化,也可以切换到没有GIL的实现。这里有一篇文章提供了更多关于这个主题的信息:

@Natecat的答案基本上是正确的,尽管有点缺乏细节和细微差别。给一个更完整的画面。假设这是您正在使用的Python实现,您需要注意以下几点:

import numpy
from mpmath import coulombf
from multiprocessing import Pool

Z = 1.
k = 1.
l = 1.
eta = -Z/k

def coulombF(r):
    return coulombf(l,eta,k*r)

pool = Pool(12)
result = pool.map_async(coulombF, numpy.arange(0.,100.,0.01))
print(result.get())
Python解释器不是完全线程安全的。为了支持多线程Python程序,有一个全局锁,称为全局解释器锁或GIL,它必须由当前线程持有,然后才能安全地访问Python对象。如果没有锁,即使是最简单的操作也可能导致多线程程序出现问题[…]

因此,只有获得GIL的线程才能对Python对象进行操作或调用Python/CAPI函数。为了模拟执行的并发性,解释器定期尝试切换线程(请参见sys.setswitchinterval()。锁也被释放,可能会阻止I/O操作,如读取或写入文件,以便其他Python线程可以同时运行

当线程是从C创建的(例如,由具有自己线程管理的第三方库创建)时,它们不持有GIL,也没有线程状态结构

注意:OpenMP的情况正是如此

如果需要从这些线程调用Python代码[…],则必须首先通过创建线程状态数据结构向解释器注册这些线程,然后获取GIL,最后存储它们的线程状态指针,然后才能开始使用Python/C API。完成后,应该重置线程状态指针,释放GIL,最后释放线程状态数据结构

PyGILState_sure()和PyGILState_Release()函数自动完成上述所有操作。从C线程调用Python的典型习惯用法是:

PyGILState_STATE gstate;
gstate = PyGILState_Ensure();

/* Perform Python actions here. */
result = CallSomeFunction();
/* evaluate result or handle exception */

/* Release the thread. No Python API allowed beyond this point. */
PyGILState_Release(gstate);

您必须实现该模式,以允许多个OpenMP线程安全地并发调用同一个CPython解释器,但您不太可能从并行化中获得太多好处,因为各种OpenMP线程将在很大程度上被阻止并发运行。

@Natecat的答案基本正确,如果有点缺乏细节和细微差别。给一个更完整的画面。假设这是您正在使用的Python实现,您需要注意以下几点:

import numpy
from mpmath import coulombf
from multiprocessing import Pool

Z = 1.
k = 1.
l = 1.
eta = -Z/k

def coulombF(r):
    return coulombf(l,eta,k*r)

pool = Pool(12)
result = pool.map_async(coulombF, numpy.arange(0.,100.,0.01))
print(result.get())
Python解释器不是完全线程安全的。为了支持多线程Python程序,有一个全局锁,称为全局解释器锁或GIL,它必须由当前线程持有,然后才能安全地访问Python对象。如果没有锁,即使是最简单的操作也可能导致多线程程序出现问题[…]

因此,只有获得GIL的线程才能对Python对象进行操作或调用Python/CAPI函数。为了模拟执行的并发性,解释器定期尝试切换线程(请参见sys.setswitchinterval()。锁也被释放,可能会阻止I/O操作,如读取或写入文件,以便其他Python线程可以同时运行

当线程是从C创建的(例如,由具有自己线程管理的第三方库创建)时,它们不持有GIL,也没有线程状态结构

注意:OpenMP的情况正是如此

如果需要从这些线程调用Python代码[…],则必须首先通过创建线程状态数据结构向解释器注册这些线程,然后获取GIL,最后存储它们的线程状态指针,然后才能开始使用Python/C API。完成后,应该重置线程状态指针,释放GIL,最后释放线程状态数据结构

PyGILState_sure()和PyGILState_Release()函数自动完成上述所有操作。从C线程调用Python的典型习惯用法是:

PyGILState_STATE gstate;
gstate = PyGILState_Ensure();

/* Perform Python actions here. */
result = CallSomeFunction();
/* evaluate result or handle exception */

/* Release the thread. No Python API allowed beyond this point. */
PyGILState_Release(gstate);

您必须实现该模式,以允许多个OpenMP线程安全地并发调用同一个CPython解释器,但您不太可能从并行化中获得太多好处,因为各种OpenMP线程将在很大程度上被阻止并发运行。

只是澄清一下,这是在尝试创建单独的线程,不是进程,对吗?是的,我使用OpenMP创建单独的线程,每个线程调用Python函数。只是为了澄清,这是试图创建单独的线程,而不是进程,对吗?是的,我使用OpenMP创建单独的线程,每个线程调用Python函数。这是错误的。请参阅
线程
多处理
模块。GIL的存在实际上是为了在解释器中允许无内部争用的多处理。线程只允许模拟并发,没有真正的多线程。多处理不是多线程处理,它的开销要大得多。为了澄清这一点,我将更改我的答案。这是一种多线程处理。线程不需要是一个单独的进程