У меня есть следующий прослушиватель на С++, который получает объект Python для распространения обратных вызовов.
class PyClient {
private:
std::vector<DipSubscription *> subs;
subsFactory *sub;
class GeneralDataListener: public SubscriptionListener {
private:
PyClient * client;
public:
GeneralDataListener(PyClient *c):client(c){
client->pyListener.attr("log_message")("Handler created");
}
void handleMessage(Subscription *sub, Data &message) {
// Lock the execution of this method
PyGILState_STATE state = PyGILState_Ensure();
client->pyListener.attr("log_message")("Data received for topic");
...
// This method ends modifying the value of the Python object
topicEntity.attr("save_value")(valueKey, extractDipValue(valueKey.c_str(), message))
// Release the lock
PyGILState_Release(state);
}
void connected(Subscription *sub) {
client->pyListener.attr("connected")(sub->getTopicName());
}
void disconnected(Subscription *sub, char* reason) {
std::string s_reason(reason);
client->pyListener.attr("disconnected")(sub->getTopicName(), s_reason);
}
void handleException(Subscription *sub, Exception &ex) {
client->pyListener.attr("handle_exception")(sub->getTopicName())(ex.what());
}
};
GeneralDataListener *handler;
public:
python::object pyListener;
PyClient(python::object pyList): pyListener(pyList) {
std::ostringstream iss;
iss << "Listener" << getpid();
sub = Sub::create(iss.str().c_str());
createSubscriptions();
}
~PyClient() {
for (unsigned int i = 0; i < subs.size(); i++) {
if (subs[i] == NULL) {
continue;
}
sub->destroySubscription(subs[i]);
}
}
};
BOOST_PYTHON_MODULE(pytest)
{
// There is no need to expose more methods as will be used as callbacks
Py_Initialize();
PyEval_InitThreads();
python::class_<PyClient>("PyClient", python::init<python::object>())
.def("pokeHandler", &PyClient::pokeHandler);
};
Затем у меня есть моя программа Python, которая выглядит так:
import sys
import time
import pytest
class Entity(object):
def __init__(self, entity, mapping):
self.entity = entity
self.mapping = mapping
self.values = {}
for field in mapping:
self.values[field] = ""
self.updated = False
def save_value(self, field, value):
self.values[field] = value
self.updated = True
class PyListener(object):
def __init__(self):
self.listeners = 0
self.mapping = ["value"]
self.path_entity = {}
self.path_entity["path/to/node"] = Entity('Name', self.mapping)
def connected(self, topic):
print "%s topic connected" % topic
def disconnected(self, topic, reason):
print "%s topic disconnected, reason: %s" % (topic, reason)
def handle_message(self, topic):
print "Handling message from topic %s" % topic
def handle_exception(self, topic, exception):
print "Exception %s in topic %s" % (exception, topic)
def log_message(self, message):
print message
def sample(self):
for path, entity in self.path_entity.iteritems():
if not entity.updated:
return False
sample = " ".join([entity.values[field] for field in dip_entity.mapping])
print "%d %s %d %s" % (0, entity.entity, 4324, sample)
entity.updated = False
return True
if __name__ == "__main__":
sys.settrace(trace)
py_listener = PyListener()
sub = pytest.PyClient(py_listener)
while True:
if py_listener.sample():
break
Итак, наконец, моя проблема заключается в том, что когда я запускаю while True в программе Python, скрипт застревает, проверяя, обновлен ли объект, и случайным образом, когда слушатель C++ пытается вызвать обратный вызов, я получаю ошибку сегментации.
То же самое, если я просто попробую time.sleep в скрипте python и вызову время выборки по времени. Я знаю, что это будет решено, если я вызову образец из кода C++, но этот скрипт будет запущен другим модулем Python, который вызовет метод образца с определенной задержкой. Таким образом, ожидаемое функционирование будет для C++ для обновления значения объекты и скрипт Python, чтобы просто прочитать их.
Я отлаживаю ошибку с помощью gdb, но трассировка стека, которую я получаю, мало что объясняет:
#0 0x00007ffff7a83717 in PyFrame_New () from /lib64/libpython2.7.so.1.0
#1 0x00007ffff7af58dc in PyEval_EvalFrameEx () from /lib64/libpython2.7.so.1.0
#2 0x00007ffff7af718d in PyEval_EvalCodeEx () from /lib64/libpython2.7.so.1.0
#3 0x00007ffff7af7292 in PyEval_EvalCode () from /lib64/libpython2.7.so.1.0
#4 0x00007ffff7b106cf in run_mod () from /lib64/libpython2.7.so.1.0
#5 0x00007ffff7b1188e in PyRun_FileExFlags () from /lib64/libpython2.7.so.1.0
#6 0x00007ffff7b12b19 in PyRun_SimpleFileExFlags () from /lib64/libpython2.7.so.1.0
#7 0x00007ffff7b23b1f in Py_Main () from /lib64/libpython2.7.so.1.0
#8 0x00007ffff6d50af5 in __libc_start_main () from /lib64/libc.so.6
#9 0x0000000000400721 in _start ()
И если отлаживать с помощью sys.trace внутри Python, последняя строка перед ошибкой сегментации всегда находится в методе примера, но она может отличаться.
Я не уверен, как я могу решить эти проблемы со связью, поэтому любые советы в правильном направлении будут высоко оценены.
Изменить Измените ссылку PyDipClient на PyClient.
Что происходит, так это то, что я запускаю программу из основного метода Python, если затем слушатель C++ пытается вызвать обратно слушателя Python, он падает с ошибкой ошибки сегментации, единственный поток, который, как мне кажется, создается, - это когда я создаю подписку, но это код из библиотеки, которую я не знаю, как именно работает.
Если я удалю все обратные вызовы для слушателя Python и принудительно вызову методы из Python (например, вызов pokehandler), все будет работать отлично.
PyDipClient
, многопоточность и т. д.). mcve поможет другим диагностировать проблему. - person Tanner Sansbury   schedule 22.07.2015