#python #c #django #python-c-api
Вопрос:
Краткое изложение проблемы
Я внедряю давно работающее расширение C/C в рамках платформы Django (вызывается через сообщение с запросом). В некоторых случаях пользователь может захотеть прервать вычисление, просто оставив/выгрузив конкретную веб-страницу, на которой вызывается расширение C. Можно ли удалить расширение из Python?
Минимальный Воспроизводимый Пример
Чтобы воспроизвести проблему, мы можем создать наше собственное расширение Фибоначчи C. Вызов в Django выглядит следующим образом:
import fibonacci
from django.views.generic.base import TemplateView
# Computation
class ComputationView(TemplateView):
template_name = 'Generic/computation_page.html'
@staticmethod
def get_results(request):
N=1000
if request.method == "POST": # When button is pressed
# Long computations
cs_min = fibonacci.fib(N)
else:
cs_min = None
return render(request, ComputationView.template_name, {'cs_min': cs_min})
В то время как код на C является:
#include <Python.h>
// Compute the Minimal cutsets
int Cfib(int n)
{
int fib1;
int fib2;
if (n < 2)
return n;
else{
Py_BEGIN_ALLOW_THREADS // Release GIL
// Some stric operations in C
Py_END_ALLOW_THREADS // Reacquire GIL
return Cfib(n-1) Cfib(n-2);
}
}
// Our Python binding to our C function
// This will take one and only one non-keyword argument
static PyObject* fib(PyObject* self, PyObject* args)
{
// instantiate our 'n' value
int n; // Order of cutset
// if our `n` value
if(!PyArg_ParseTuple(args, "i", amp;n))
return NULL;
// return our computed fib number
return Py_BuildValue("i", Cfib(n));
}
// Our Module's Function Definition struct
// We require this `NULL` to signal the end of our method
// definition
static PyMethodDef myMethods[] = {
{ "fib", fib, METH_VARARGS | METH_KEYWORDS, "Computes Fibonacci" },
{ NULL, NULL, 0, NULL }
};
// Our Module Definition struct
static struct PyModuleDef fibonacci = {
PyModuleDef_HEAD_INIT,
"fibonacci",
"Test Module",
-1,
myMethods
};
// Initializes our module using our above struct
PyMODINIT_FUNC PyInit_fibonacci(void)
{
return PyModule_Create(amp;fibonacci);
}
As you can see, the user can do other actions thanks to the GIL management ( ALLOW_THREADS
) while the C function is running.
Alternative approach I have tried
Since I have been unable to kill the running function, I have tried directly killing the thread in charge of the C function by doing:
import psutil
# Get the process
current_process = psutil.Process()
# Iterate the threads and find the one which has spent more time
max_time = 0
ind_max = 0
for ind, thread in enumerate(current_process.threads()):
if thread.user_time > max_time:
max_time = thread.user_time
ind_max = ind
# Kill the thread with more time
# This only works in Linux !!!!!
if platform.system()!= "Windows" and max_time > 10:
signal.pthread_kill(current_process.threads()[ind_max].id, signal.SIGINT)
However, this approach kills not only the responsible thread, but the entire Django process.
Possible alternative approach
I am aware that you can arise exceptions inside the C extension. For instance:
PyErr_SetString(PyExc_TypeError, "Custom error message");
Если бы я мог прочитать значение, хранящееся в базе данных, или передать каким-либо образом аргумент (по ссылке?) чтобы функция могла непрерывно считываться во время работы, я мог бы достичь той же цели-остановить функцию. Однако я понятия не имею, как это сделать.