Запуск Python3 в Linux, который обращается к общей библиотеке C (.so). Библиотека C будет запускать потоки по мере необходимости. У меня возникла проблема с ошибками сегментации при доступе к API C во время работы потоков. Проблема возникает только в том случае, если поток C вызывает функцию обратного вызова в Python. Если поток C выполняется без вызова обратного вызова, проблем нет. В приведенном ниже коде строки Python print(GetData(128)) получат строку и выведут ее на консоль, прежде чем вызвать ошибку сегмента. Опять же, я получаю ошибку сегментации GetData только в том случае, если поток работает И вызывает событие или обратный вызов. Другие API C, которые возвращают символ char*, но не принимают параметров, прекрасно работают при работающем потоке.
Python:
Код: Выделить всё
# Within a class, but not shown for simplicity.
EVENT = CFUNCTYPE(c_void_p, c_int)
lib = CDLL("library.so")
lib.GetData.restype = c_uint
lib.GetData.argtypes = [c_char_p, c_uint]
lib.SetEvent.argtypes = [EVENT]
def GetData(count):
buf = create_string_buffer(count)
lib.GetData(buf, count)
return buf.value.decode("utf-8")
def SetEvent(ev):
lib.SetEvent(ev)
# Code outside of class.
def Event(e):
print("Event " + str(e))
if e == 2:
print(GetData(128)) # Event works fine until I call this.
SetEvent(EVENT(Event))
cnt = 0
while cnt < 10:
print(GetData(128)) # Works if I do not call SetEvent.
cnt = cnt + 1
sleep(1)
Код: Выделить всё
typedef void(*Event)(int i);
Event efunc = nullptr;
static void ThreadFunc()
{
int i = 0;
while(true)
{
if (efunc != nullptr) { efunc(++i); } // Raise event.
if (i > 10) { i = 0; }
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
extern "C" unsigned int GetData(char* data, unsigned int count)
{
const char* str = "Random data for testing";
std::strcpy(data, str);
return std::strlen(str);
}
extern "C" void SetEvent(Event e)
{
efunc = e;
if (efunc != nullptr) { myThread = std::thread(ThreadFunc); }
}
Обновить
Я добавил библиотеку Python в общую библиотеку C/C++ и внес следующие изменения в код (см. выше).
C/C++:
Код: Выделить всё
static void ThreadFunc()
{
int i = 0;
while(true)
{
if (efunc != nullptr)
{
PyGILState_STATE state = PyGILState_Ensure();
// Py_BEGIN_ALLOW_THREADS
efunc(++i); // Raise event.
// Py_END_ALLOW_THREADS
PyGILState_Release(state);
}
if (i > 10) { i = 0; }
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
extern "C" void SetEvent(Event e)
{
efunc = e;
if (efunc != nullptr)
{
if (!PyEval_ThreadsInitialized())
{
PyEval_InitThreads();
}
myThread = std::thread(ThreadFunc)
}
}
Мобильная версия