मैं पाइथन के लिए सी एक्सटेंशन लिख रहा हूं, जो डेटा पर चलने पर ग्लोबल इंटरप्रेटर लॉक को रिलीज़ करना चाहिए। मुझे लगता है कि मैंने जीआईएल के तंत्र को काफी अच्छी तरह से समझ लिया है, लेकिन एक सवाल बनी हुई है: क्या मैं पाइथन ऑब्जेक्ट में डेटा तक पहुंच सकता हूं जबकि थ्रेड जीआईएल का मालिक नहीं है? उदाहरण के लिए, मैं सी फ़ंक्शन में एक (बड़ी) NumPy सरणी से डेटा पढ़ना चाहता हूं, जबकि मैं अभी भी अन्य थ्रेड को अन्य CPU कोर पर अन्य चीजों को करने की अनुमति देना चाहता हूं। सी समारोह चाहिए अजगर कार्यों ग्लोबल इंटरप्रेटर लॉक और डेटा तक पहुंच (उदाहरण के लिए, NumPy arrays के लिए)
- रिलीज के साथ
Py_BEGIN_ALLOW_THREADS
- पढ़ने और डेटा पर काम जीआईएल
Py_END_ALLOW_THREADS
क्या यह सुरक्षित है? बेशक, अन्य धागे को वेरिएबल्स को बदलने की आवश्यकता नहीं होती है जो सी फ़ंक्शन का उपयोग करता है। लेकिन शायद त्रुटियों के लिए एक छिपा स्रोत है: क्या पाइथन दुभाषिया एक वस्तु को स्थानांतरित कर सकता है, उदाहरण के लिए। किसी प्रकार के कचरे के संग्रह से, जबकि सी फ़ंक्शन एक अलग धागे में काम करता है?
कम से कम उदाहरण के साथ प्रश्न को चित्रित करने के लिए, नीचे (न्यूनतम लेकिन पूर्ण) कोड पर विचार करें।
gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -fPIC -I/usr/lib/pymodules/python2.7/numpy/core/include -I/usr/include/python2.7 -c gilexample.c -o gilexample.o
gcc -pthread -shared gilexample.o -o gilexample.so
साथ (लिनक्स पर) यह संकलन और अजगर पर परीक्षण करना
import gilexample
gilexample.sum([1,2,3])
साथ Py_BEGIN_ALLOW_THREADS
और Py_END_ALLOW_THREADS
सुरक्षित के बीच कोड है? यह एक पायथन ऑब्जेक्ट की सामग्री तक पहुंचता है, और मैं स्मृति में (संभावित रूप से बड़ी) सरणी को डुप्लिकेट नहीं करना चाहता हूं।
#include <Python.h>
#include <numpy/arrayobject.h>
// The relevant function
static PyObject * sum(PyObject * const self, PyObject * const args) {
PyObject * X;
PyArg_ParseTuple(args, "O", &X);
PyObject const * const X_double = PyArray_FROM_OTF(X, NPY_DOUBLE, NPY_ALIGNED);
npy_intp const size = PyArray_SIZE(X_double);
double * const data = (double *) PyArray_DATA(X_double);
double sum = 0;
Py_BEGIN_ALLOW_THREADS // IS THIS SAFE?
npy_intp i;
for (i=0; i<size; i++)
sum += data[i];
Py_END_ALLOW_THREADS
Py_DECREF(X_double);
return PyFloat_FromDouble(sum);
}
// Python interface code
// List the C methods that this extension provides.
static PyMethodDef gilexampleMethods[] = {
{"sum", sum, METH_VARARGS},
{NULL, NULL, 0, NULL} /* Sentinel - marks the end of this structure */
};
// Tell Python about these methods.
PyMODINIT_FUNC initgilexample(void) {
(void) Py_InitModule("gilexample", gilexampleMethods);
import_array(); // Must be present for NumPy.
}
मैंने अतीत में इस तरह की चीजें की, और मैंने पाया कि यह काम करने का सबसे आसान तरीका आपके सी कार्यों को कॉल करने के लिए 'ctypes' का उपयोग कर रहा है। अपने सी कार्यों को पाइथन या न्यूमपी के संदर्भ के बिना एक शुद्ध सी इंटरफ़ेस दें, और पाइथन में छोटे रैपर लिखें जो NumPy arrays स्वीकार करते हैं और उन्हें उपयुक्त सी पैरामीटर में अनुवाद करते हैं। मैंने [इस उत्तर] में यह कैसे किया है इस पर एक उदाहरण दिया है (http://stackoverflow.com/questions/5862915/passing-numpy-arrays-to-ac-function-for-input-and-output/5868051#5868051)। –
@ सेवन: क्या आप जानते हैं कि 'ctypes' मेमोरी में सरणी की एक कार्यशील प्रति बनाता है या नहीं? (1) हां। इस मामले में, मैं इसे नहीं चाहता क्योंकि मैं बड़े इनपुट सरणी से निपट रहा हूं। (2) संख्याफिर मेरा सवाल है कि क्या आप जीआईएल उठा सकते हैं वैध है। हालांकि, मामले में (2), 'ctypes' व्यवहार एक संकेत होगा कि जीआईएल उठाना शायद समस्याग्रस्त नहीं है, कोड में भी जो ctypes का उपयोग नहीं करता है। क्या किसी को पता है कि (1) या (2) धारण करता है? – Daniel
नहीं, 'ctypes' सरणी की एक प्रति नहीं बनाता है। और यह आपके लिए जीआईएल जारी करता है, इसलिए आपको इसकी परवाह नहीं है। 'Ctypes' का उपयोग करने का लाभ सादगी है - आपको अभी भी पाइथन में NumPy सरणी से सभी आवश्यक मेटा-जानकारी निकालना है, और जीआईएल को सही समय पर रिलीज़ किया गया है। मैंने इस दृष्टिकोण का उपयोग कई धागे से न्यूमपी सरणी में डेटा तक पहुंचने के लिए किया था। (ध्यान दें कि एक ही मेमोरी में समवर्ती लेखन पहुंच कभी नहीं बचाई जाती है।) –