我有一个c ++函数,它生成一个子线程来提供异步输出(最终这将用于实现报告多线程算法状态的进度监视器)。我希望子线程能够输出到任何地方,包括IPython笔记本。出于这个原因,我为子线程提供了一个接收字符串的回调例程。如果我想输出到Python标准输出,我使用指向暴露的cython例程的指针(使用api
关键字)初始化此回调。
我花了一些时间才能正确实现允许我从子线程执行Python代码的所有GIL簿记,但最终我得到的代码在经典的Python提示符下工作。工作意味着每半秒(或多或少)我在Python提示符下从子线程获得更新输出。
问题在于,当我从IPython qtconsole或IPython笔记本执行此代码时,输出在函数返回之前不会出现。此时所有输出都会立即显示(这显然不适用于进度监视器)。
我怀疑这与GIL如何从Python内核传递到IPython内核端客户端有关?如何允许内核端IPython客户端将新输出发送到笔记本?
pymontor.hpp
#include "Python.h"
#include "compel_api.h"
void printSomeOutput(PyObject *callable);
void printSomeOutputMT(PyObject *callable);
pymonitor.cpp
#include "pymonitor.hpp"
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/thread.hpp>
struct Worker {
PyObject *callback;
size_t n;
void operator()()
{
std::string msg = "Hello!";
for(size_t i = 0; i < n; ++i)
{
boost::this_thread::sleep(boost::posix_time::milliseconds(500));
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
cy_print_message(msg, callable);
PyGILState_Release(gstate);
}
}
};
void printSomeOutputMT(PyObject *callable)
{
import_compel();
Py_INCREF(callable);
Worker w;
w.callable = callable;
w.n = 5;
PyEval_InitThreads();
Py_BEGIN_ALLOW_THREADS
boost::thread thr(w);
thr.join();
Py_END_ALLOW_THREADS
Py_DECREF(callable);
}
compel.pdx
的内容:
cdef extern from "pymonitor.hpp" namespace "compel":
cdef void printSomeOutputMT(object callable)
compel.pyx
的内容:
cdef public api cy_print_message(string message, object callback):
callback(message)
def test_printMT(object callback):
printSomeOutputMT(callback)
在Python / IPython-notebook命令行测试:
from compel import *
def f(m):
import sys
print(m)
sys.stdout.flush()
test_printMT(f)