【问题标题】:C code embedded python callback functionC代码内嵌python回调函数
【发布时间】:2016-03-01 07:53:53
【问题描述】:

C代码内嵌python回调函数,通过回调将数据放入python队列,当我从队列中获取数据时,速度很慢。

例子:

这样的代码

static int wrap_func(const int a, const unsigned char *b)
{
  long ret;
  PyObject *arglist;
  PyObject * result = NULL;

  arglist = Py_BuildValue("(s#)", b, a);
  result = PyEval_CallObject(my_callback, arglist);

  /* evaluate result or handle exception */
  ret = PyInt_AsLong(result);
  if (result == NULL)
    return -1;
  Py_DECREF(result);

  return ret;
}

void produce_data()
{
    while(1){
        //produce data to buffer, len is buffer length
        //call callback func 
        wrap_func(buffer, len);
    }
}

把这个c代码编译成so like mywrap.so,然后在python中导入这个so python代码如下:

import multiprocessing
import mywarp   # mywrap.so

class WorkerThread_a(threading.Thread):
    def __init__(self, workQueue):
        threading.Thread.__init__(self)
        self.workQueue = workQueue
        self.setDaemon(True)
    def run(self):
        while 1:
            try:
                recvdata = self.workQueue.get(block=False)
            except Queue.Empty:
                continue
            #do sth use recvdata

workQueue = multiprocessing.Queue()

def callback_func(a):
    if a:
        workQueue.put(a)
    return 0

def main():
    tmp = WorkerThread_a(workQueue)
    tmp.start()
    mywarp.set_callback(callback_func)
    mywarp.decode_audio()

main()

在python线程中,我从队列中获取数据,但是我获取数据非常慢,但是在c中,生成数据并通过python回调函数快速放入队列。

如何像在纯 Python 代码中一样快速从队列中获取数据。

【问题讨论】:

    标签: python c callback queue cython


    【解决方案1】:

    我认为发生的情况是您的 C 代码永远不会释放全局解释器锁 (GIL),因此您的 Python 代码永远没有机会运行。当您在 Python 代码中运行多个线程时,它们会自动在它们之间交换 GIL,从而平等地共享时间,但是如果没有您对 C 代码的干预,这不会发生。

    如果您在 C 代码中的每个循环中获取并释放一次 GIL,它可能会更好地工作(即使您不做任何不需要需要它的事情)。我真正做的只是在函数的开头添加了宏 Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS

    static int wrap_func(const int a, const unsigned char *b)
    {
      Py_BEGIN_ALLOW_THREADS /* ADDED! */
      /* This should give the chance for the main Python thread
      to run, even though no code goes here */
      Py_END_ALLOW_THREADS /* ADDED */
    
      long ret;
      PyObject *arglist;
      PyObject * result = NULL;
    
      arglist = Py_BuildValue("(s#)", b, a);
      result = PyEval_CallObject(my_callback, arglist);
    
      /* evaluate result or handle exception */
      ret = PyInt_AsLong(result);
    
      /* This is chang */
      if (result == NULL)
        return -1;
      Py_DECREF(result);
    
    
      return ret;
    }
    

    (我应该说 - 这是一个未经检验的猜测,我 90% 肯定是对的,但我以前错了!)

    【讨论】:

    • (我已经编辑了这个 - 我的原始代码有两个宏错误的方式,所以不会工作 - 对不起!)
    • 你的建议很棒,很有效!非常感谢。我按照您的建议运行代码,它可以工作,但是当我将 Py_BEGIN_ALLOW_THREADS 放在函数的开头,并在返回之前放置 Py_END_ALLOW_THREADS 时。我运行它,是段错误,这是为什么?
    • 那是我搞错了! (现已修复)。不允许在 Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS 之间进行任何 Python api 调用。所以这意味着任何以Py 开头的函数都不起作用。不幸的是,这些代码实际上都不能并行运行——我刚刚添加了一个可以在 C 模块和 Python 代码之间交换的地方。
    猜你喜欢
    • 1970-01-01
    • 2011-11-24
    • 1970-01-01
    • 1970-01-01
    • 2021-06-14
    • 2020-11-22
    • 2013-05-26
    • 2022-01-25
    • 1970-01-01
    相关资源
    最近更新 更多