2012-01-11 35 views
7

Tôi đang viết một phần mở rộng C cho Python, sẽ phát hành Khóa thông dịch toàn cục trong khi nó hoạt động trên dữ liệu. Tôi nghĩ rằng tôi đã hiểu cơ chế của GIL khá tốt, nhưng vẫn còn một câu hỏi: Tôi có thể truy cập dữ liệu trong một đối tượng Python trong khi luồng không sở hữu GIL không? Ví dụ, tôi muốn đọc dữ liệu từ một mảng lớn (NumPy) trong hàm C trong khi tôi vẫn muốn cho phép các luồng khác làm những việc khác trên các lõi CPU khác. Chức năng C nênKhóa phiên dịch toàn cầu và truy cập dữ liệu (ví dụ: đối với mảng NumPy)

  • phát hành GIL với Py_BEGIN_ALLOW_THREADS
  • đọc và làm việc trên các dữ liệu mà không cần sử dụng Python chức năng
  • thậm chí ghi dữ liệu vào mảng NumPy xây dựng trước đây
  • reacquire các GIL với Py_END_ALLOW_THREADS

Điều này có an toàn không? Tất nhiên, các luồng khác không được phép thay đổi các biến mà hàm C sử dụng. Nhưng có thể có một nguồn ẩn cho các lỗi: có thể trình thông dịch Python di chuyển một đối tượng, ví dụ. bởi một số loại bộ sưu tập rác, trong khi chức năng C hoạt động trên nó trong một chủ đề riêng biệt?

Để minh họa câu hỏi với ví dụ tối thiểu, hãy xem xét mã (tối thiểu nhưng hoàn thành) bên dưới. Biên dịch nó (trên Linux) với

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 

và thử nghiệm nó trong Python với

import gilexample 
gilexample.sum([1,2,3]) 

Là mã giữa Py_BEGIN_ALLOW_THREADSPy_END_ALLOW_THREADS an toàn không? Nó truy cập nội dung của một đối tượng Python, và tôi không muốn sao chép mảng (có thể lớn) trong bộ nhớ.

#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. 
} 
+2

Tôi đã làm những việc như thế này trong quá khứ và tôi thấy rằng cách dễ nhất để thực hiện công việc này là sử dụng 'ctypes' để gọi các hàm C của bạn. Cung cấp cho các hàm C của bạn một giao diện C thuần túy mà không cần tham chiếu đến Python hoặc NumPy và viết các trình bao bọc tầm thường trong Python chấp nhận các mảng NumPy và dịch chúng thành các tham số C thích hợp. Tôi đã đưa ra một ví dụ về cách làm điều này trong [câu trả lời này] (http://stackoverflow.com/questions/5862915/passing-numpy-arrays-to-ac-function-for-input-and-output/5868051#5868051). –

+0

@Sven: Bạn có biết liệu 'ctypes' có tạo bản sao làm việc của mảng trong bộ nhớ không? (1) Có. Trong trường hợp này, tôi không muốn nó vì tôi đang xử lý các mảng đầu vào lớn. (2) Không.Sau đó, câu hỏi của tôi cho dù bạn có thể nâng GIL vẫn hợp lệ. Tuy nhiên, trong trường hợp (2), hành vi 'ctypes' sẽ là một gợi ý rằng việc nâng cấp GIL có lẽ không có vấn đề, cũng như trong mã không sử dụng ctypes. Có ai biết liệu (1) hoặc (2) nắm giữ? – Daniel

+5

Không, 'ctypes' không tạo bản sao của các mảng. Và nó phát hành GIL cho bạn, vì vậy bạn không phải quan tâm đến nó. Lợi thế của việc sử dụng 'ctypes' là sự đơn giản - bạn phải trích xuất tất cả các thông tin cần thiết từ mảng NumPy trong khi vẫn còn trong Python, và GIL được phát hành vào đúng thời điểm. Tôi đã sử dụng phương pháp này để đồng thời truy cập dữ liệu trong mảng NumPy từ nhiều luồng. (Lưu ý rằng ghi đồng thời truy cập vào cùng một bộ nhớ không bao giờ được lưu.) –

Trả lời

4

Tôi có thể truy cập dữ liệu trong một đối tượng Python trong khi thread không sở hữu GIL?

Không, bạn không thể.

+0

Điều đó có nghĩa là tôi luôn phải sao chép nội dung của một mảng NumPy, ngay cả khi tôi chỉ muốn đọc dữ liệu trong một chuỗi? Tôi hy vọng rằng có một cách xung quanh nó! Bất kì lời đề nghị nào? – Daniel

6

Điều này có an toàn không?

Đúng, không. Tôi nghĩ bạn nên chuyển các cuộc gọi đến PyArray_SIZEPyArray_DATA bên ngoài khối GIL ít hơn; nếu bạn làm điều đó, bạn sẽ chỉ hoạt động trên dữ liệu C. Bạn cũng có thể muốn tăng số lượng tham chiếu trên đối tượng trước khi đi vào khối GIL-less và giảm nó sau đó.

Sau khi chỉnh sửa, nó sẽ an toàn. Đừng quên giảm số lượng tham chiếu sau đó.

+0

Cảm ơn bạn đã nhận xét về 'PyArray_SIZE' và' PyArray_DATA'. Đó là một sai lầm. Tôi đã chỉnh sửa câu hỏi của mình và di chuyển các lệnh bên ngoài khối nơi GIL được phát hành. – Daniel

+0

Bây giờ: là mã trong phiên bản sửa đổi của nó có an toàn không? Bạn có thể giải thích lý do tại sao tăng số lượng tham chiếu sẽ thay đổi mọi thứ? – Daniel

+0

@Daniel: số lượng tham chiếu có thể là cần thiết (mặc dù tôi không hoàn toàn chắc chắn trong trường hợp này) vì không có chủ đề nào khác phải giải quyết mảng. –

Các vấn đề liên quan