1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
.. include:: header.txt
========================
Shared ctypes objects
========================
The `processing.sharedctypes` module provides functions for allocating
ctypes objects from shared memory which can be inherited by child
processes. (See the standard library's documentation for details of
the `ctypes` package.)
The functions in the module are
`RawArray(typecode_or_type, size_or_initializer)`
Returns a ctypes array allocated from shared memory.
`typecode_or_type` determines the type of the elements of the
returned array: it is either a ctypes type or a one character
typecode of the kind used by the `array` module. If
`size_or_initializer` is an integer then it determines the
length of the array, and the array will be initially zeroed.
Otherwise `size_or_initializer` is a sequence which is used to
initialize the array and whose length determines the length of
the array.
Note that setting and getting an element is potentially
non-atomic -- use Array() instead to make sure that access is
automatically synchronized using a lock.
`RawValue(typecode_or_type, *args)`
Returns a ctypes object allocated from shared memory.
`typecode_or_type` determines the type of the returned object:
it is either a ctypes type or a one character typecode of the
kind used by the `array` module. `*args` is passed on to the
constructor for the type.
Note that setting and getting the value is potentially
non-atomic -- use Value() instead to make sure that access is
automatically synchronized using a lock.
Note that an array of `ctypes.c_char` has `value` and
`rawvalue` attributes which allow one to use it to store and
retrieve strings -- see documentation for `ctypes`.
`Array(typecode_or_type, size_or_initializer, **, lock=True)`
The same as `RawArray()` except that depending on the value of
`lock` a process-safe synchronization wrapper may be returned
instead of a raw ctypes array.
If `lock` is true (the default) then a new lock object is
created to synchronize access to the value. If `lock` is a
`Lock` or `RLock` object then that will be used to synchronize
access to the value. If `lock` is false then access to the
returned object will not be automatically protected by a lock,
so it will not necessarily be "process-safe".
Note that `lock` is a keyword only argument.
`Value(typecode_or_type, *args, **, lock=True)`
The same as `RawValue()` except that depending on the value of
`lock` a process-safe synchronization wrapper may be returned
instead of a raw ctypes object.
If `lock` is true (the default) then a new lock object is
created to synchronize access to the value. If `lock` is a
`Lock` or `RLock` object then that will be used to synchronize
access to the value. If `lock` is false then access to the
returned object will not be automatically protected by a lock,
so it will not necessarily be "process-safe".
Note that `lock` is a keyword only argument.
`copy(obj)`
Returns a ctypes object allocated from shared memory which is
a copy of the ctypes object `obj`.
`synchronized(obj, lock=None)`
Returns a process-safe wrapper object for a ctypes object
which uses `lock` to synchronize access. If `lock` is `None`
then a `processing.RLock` object is created automatically.
A synchronized wrapper will have two methods in addition to
those of the object it wraps: `getobj()` returns the wrapped
object and `getlock()` returns the lock object used for
synchronization.
Note that accessing the ctypes object through the wrapper can
be a lot slower than accessing the raw ctypes object.
Equivalences
============
The table below compares the syntax for creating shared ctypes objects
from shared memory with the normal ctypes syntax. (In the table
`MyStruct` is some subclass of `ctypes.Structure`.)
==================== ========================== ===========================
ctypes sharedctypes using type sharedctypes using typecode
==================== ========================== ===========================
c_double(2.4) RawValue(c_double, 2.4) RawValue('d', 2.4)
MyStruct(4, 6) RawValue(MyStruct, 4, 6)
(c_short * 7)() RawArray(c_short, 7) RawArray('h', 7)
(c_int * 3)(9, 2, 8) RawArray(c_int, (9, 2, 8)) RawArray('i', (9, 2, 8))
==================== ========================== ===========================
Example
=======
Below is an example where a number of ctypes objects are modified by a
child process ::
from processing import Process, Lock
from processing.sharedctypes import Value, Array
from ctypes import Structure, c_double
class Point(Structure):
_fields_ = [('x', c_double), ('y', c_double)]
def modify(n, x, s, A):
n.value **= 2
x.value **= 2
s.value = s.value.upper()
for p in A:
p.x **= 2
p.y **= 2
if __name__ == '__main__':
lock = Lock()
n = Value('i', 7)
x = Value(ctypes.c_double, 1.0/3.0, lock=False)
s = Array('c', 'hello world', lock=lock)
A = Array(Point, [(1.875,-6.25), (-5.75,2.0), (2.375,9.5)], lock=lock)
p = Process(target=modify, args=(n, x, s, A))
p.start()
p.join()
print n.value
print x.value
print s.value
print [(p.x, p.y) for p in A]
The results printed are ::
49
0.1111111111111111
HELLO WORLD
[(3.515625, 39.0625), (33.0625, 4.0), (5.640625, 90.25)]
.. admonition:: Avoid sharing pointers
Although it is posible to store a pointer in shared memory
remember that this will refer to a location in the address space
of a specific process. However, the pointer is quite likely to be
invalid in the context of a second process and trying to
dereference the pointer from the second process may cause a crash.
.. _Prev: pool-objects.html
.. _Up: processing-ref.html
.. _Next: connection-ref.html
|