multiprocessing.shared_memory --- حافظه مشترک برای دسترسی مستقیم بین فرآیندها¶
کد منبع: Lib/multiprocessing/shared_memory.py
اضافه شده در نسخهی 3.8.
این ماژول یک کلاس، SharedMemory، را برای تخصیص و مدیریت حافظه مشترکی فراهم میکند که توسط یک یا چند فرایند بر روی یک ماشین چند هستهای یا چندپردازندهای متقارن (SMP) قابل دسترسی باشد. برای کمک به مدیریت چرخه عمر حافظه مشترک، بهویژه بین فرایندهای مجزا، یک زیرکلاس از BaseManager، یعنی SharedMemoryManager، نیز در ماژول multiprocessing.managers ارائه شده است.
در این ماژول، حافظه مشترک به بلوکهای حافظه مشترک «سبک POSIX» اشاره دارد (هرچند لزوماً بهطور صریح به این صورت پیادهسازی نشده است) و به «حافظه مشترک توزیعشده» اشاره نمیکند. این سبک از حافظه مشترک به فرایندهای مجزا اجازه میدهد که بهطور بالقوه در یک ناحیه مشترک (یا به اشتراک گذاشتهشده) از حافظه فرار بخوانند و بنویسند. فرایندها بهطور قراردادی محدودند که فقط به فضای حافظه فرایند خود دسترسی داشته باشند، اما حافظه مشترک امکان اشتراکگذاری دادهها بین فرایندها را فراهم میکند و نیاز به ارسال پیامهای حاوی آن دادهها بین فرایندها بهعنوان جایگزین را از بین میبرد. اشتراکگذاری مستقیم دادهها از طریق حافظه میتواند در مقایسه با اشتراکگذاری دادهها از طریق دیسک، سوکت یا سایر ارتباطاتی که به سریالسازی/سریالزدایی و کپی دادهها نیاز دارند، مزایای عملکردی قابلتوجهی فراهم کند.
- class multiprocessing.shared_memory.SharedMemory(name=None, create=False, size=0, *, track=True)¶
نمونهای از کلاس
SharedMemoryبرای ایجاد یک بلوک حافظهی مشترک جدید یا اتصال به یک بلوک حافظهی مشترک موجود ایجاد کنید. به هر بلوک حافظهی مشترک یک نام یکتا اختصاص داده میشود. به این ترتیب، یک فرایند میتواند یک بلوک حافظهی مشترک با یک نام مشخص ایجاد کند و فرایندی دیگر میتواند با استفاده از همان نام به همان بلوک حافظهی مشترک متصل شود.بهعنوان منبعی برای اشتراکگذاری دادهها بین فرایندها، بلوکهای حافظه مشترک ممکن است پس از پایان فرایند اصلی که آنها را ایجاد کرده است همچنان باقی بمانند. هنگامی که یک فرایند دیگر نیازی به دسترسی به یک بلوک حافظه مشترک ندارد، در حالی که ممکن است هنوز فرایندهای دیگر به آن نیاز داشته باشند، باید متد
close()فراخوانی شود. هنگامی که یک بلوک حافظه مشترک دیگر مورد نیاز هیچ فرایندی نیست، باید متدunlink()برای اطمینان از پاکسازی مناسب فراخوانی شود.- پارامترها:
name (str | None) -- نام یکتای حافظه مشترک درخواستی، که بهصورت یک رشته مشخص میشود. هنگام ایجاد یک بلوک حافظه مشترک جدید، اگر
None(پیشفرض) برای نام ارائه شود، یک نام جدید تولید خواهد شد.create (bool) -- کنترل میکند که یک بلوک حافظه اشتراکی جدید ایجاد شود (
True) یا یک بلوک حافظه اشتراکی موجود متصل شود (False).size (int) -- تعداد بایتهای درخواستشده هنگام ایجاد یک بلوک حافظه مشترک جدید. از آنجا که برخی سکوها تکههای حافظه را بر اساس اندازه صفحه حافظه آن سکو تخصیص میدهند، اندازه دقیق بلوک حافظه مشترک ممکن است بزرگتر یا برابر با اندازه درخواستشده باشد. هنگام اتصال به یک بلوک حافظه مشترک موجود، پارامتر size نادیده گرفته میشود.
track (bool) -- هنگامی که
Trueباشد، در سکوهایی که سیستمعامل این کار را بهطور خودکار انجام نمیدهد، بلوک حافظهی اشتراکی در یک فرایند پیگیری منابع ثبت میشود. فرایند پیگیری منابع، پاکسازی مناسب حافظهی اشتراکی را تضمین میکند، حتی اگر همهی فرایندهای دیگری که به حافظه دسترسی دارند، بدون انجام این کار خارج شوند. فرایندهای پایتونی که با استفاده از امکاناتmultiprocessingاز یک جد مشترک ایجاد شدهاند، یک فرایند پیگیری منابع واحد را به اشتراک میگذارند و طول عمر بخشهای حافظهی اشتراکی بهطور خودکار در میان این فرایندها مدیریت میشود. فرایندهای پایتونی که به هر روش دیگری ایجاد شدهاند، هنگام دسترسی به حافظهی اشتراکی با فعال بودن track، فرایند پیگیری منابع اختصاصی خود را دریافت خواهند کرد. این امر باعث میشود حافظهی اشتراکی توسط فرایند پیگیری منابعِ اولین فرایندی که خاتمه مییابد، حذف شود. برای اجتناب از این مشکل، کاربرانsubprocessیا فرایندهای پایتونی مستقل باید هنگامی که از قبل فرایند دیگری برای انجام ثبت و نگهداری وجود دارد، track را رویFalseتنظیم کنند. track در ویندوز در نظر گرفته نمیشود؛ ویندوز پیگیری خودش را دارد و هنگامی که همهی دستههای آن بسته شده باشند، حافظهی اشتراکی را بهطور خودکار حذف میکند.
تغییر یافته در نسخهی 3.13: پارامتر track افزوده شد.
- close()¶
توصیفگر/دستهی فایلِ حافظهی مشترک را از این نمونه ببندید. هنگامی که دیگر نیازی به دسترسی به بلوک حافظهی مشترک از این نمونه نباشد، باید
close()فراخوانی شود. بسته به سیستمعامل، حتی اگر همهی دستههای آن بسته شده باشند، ممکن است حافظهی زیرین آزاد شود یا نشود. برای اطمینان از پاکسازی صحیح، از متدunlink()استفاده کنید.
- unlink()¶
بلوک حافظه مشترک زیربنایی را حذف میکند. این متد باید فقط ۱ بار بهازای هر بلوک حافظه مشترک فراخوانی شود، صرفنظر از تعداد دستههای آن، حتی در فرآیندهای دیگر. میتوان
unlink()وclose()را با هر ترتیبی فراخوانی کرد، اما تلاش برای دسترسی به دادههای درون یک بلوک حافظه مشترک پس ازunlink()ممکن است به خطاهای دسترسی به حافظه منجر شود، بسته به پلتفرم.این متد در ویندوز تأثیری ندارد؛ تنها راه حذف یک بلوک حافظه مشترک در آن، بستن تمام دستهها است.
- buf¶
یک memoryview از محتویات بلوک حافظهی مشترک.
- name¶
دسترسی فقطخواندنی به نام یکتای بلوک حافظهی مشترک.
- size¶
دسترسی فقطخواندنی به اندازهی بلوک حافظه مشترک بر حسب بایت.
مثال زیر استفاده سطح پایین از نمونههای SharedMemory را نشان میدهد:
>>> from multiprocessing import shared_memory
>>> shm_a = shared_memory.SharedMemory(create=True, size=10)
>>> type(shm_a.buf)
<class 'memoryview'>
>>> buffer = shm_a.buf
>>> len(buffer)
10
>>> buffer[:4] = bytearray([22, 33, 44, 55]) # Modify multiple at once
>>> buffer[4] = 100 # Modify single byte at a time
>>> # Attach to an existing shared memory block
>>> shm_b = shared_memory.SharedMemory(shm_a.name)
>>> import array
>>> array.array('b', shm_b.buf[:5]) # Copy the data into a new array.array
array('b', [22, 33, 44, 55, 100])
>>> shm_b.buf[:5] = b'howdy' # Modify via shm_b using bytes
>>> bytes(shm_a.buf[:5]) # Access via shm_a
b'howdy'
>>> shm_b.close() # Close each SharedMemory instance
>>> shm_a.close()
>>> shm_a.unlink() # Call unlink only once to release the shared memory
مثال زیر یک کاربرد عملی از کلاس SharedMemory با آرایههای NumPy را نشان میدهد، که در آن همان numpy.ndarray از دو پوستهی پایتون جداگانه قابل دسترسی است:
>>> # In the first Python interactive shell
>>> import numpy as np
>>> a = np.array([1, 1, 2, 3, 5, 8]) # Start with an existing NumPy array
>>> from multiprocessing import shared_memory
>>> shm = shared_memory.SharedMemory(create=True, size=a.nbytes)
>>> # Now create a NumPy array backed by shared memory
>>> b = np.ndarray(a.shape, dtype=a.dtype, buffer=shm.buf)
>>> b[:] = a[:] # Copy the original data into shared memory
>>> b
array([1, 1, 2, 3, 5, 8])
>>> type(b)
<class 'numpy.ndarray'>
>>> type(a)
<class 'numpy.ndarray'>
>>> shm.name # We did not specify a name so one was chosen for us
'psm_21467_46075'
>>> # In either the same shell or a new Python shell on the same machine
>>> import numpy as np
>>> from multiprocessing import shared_memory
>>> # Attach to the existing shared memory block
>>> existing_shm = shared_memory.SharedMemory(name='psm_21467_46075')
>>> # Note that a.shape is (6,) and a.dtype is np.int64 in this example
>>> c = np.ndarray((6,), dtype=np.int64, buffer=existing_shm.buf)
>>> c
array([1, 1, 2, 3, 5, 8])
>>> c[-1] = 888
>>> c
array([ 1, 1, 2, 3, 5, 888])
>>> # Back in the first Python interactive shell, b reflects this change
>>> b
array([ 1, 1, 2, 3, 5, 888])
>>> # Clean up from within the second Python shell
>>> del c # Unnecessary; merely emphasizing the array is no longer used
>>> existing_shm.close()
>>> # Clean up from within the first Python shell
>>> del b # Unnecessary; merely emphasizing the array is no longer used
>>> shm.close()
>>> shm.unlink() # Free and release the shared memory block at the very end
- class multiprocessing.managers.SharedMemoryManager([address[, authkey]])¶
یک کلاس فرعی از
multiprocessing.managers.BaseManagerکه میتوان از آن برای مدیریت بلوکهای حافظه مشترک بین فرایندها استفاده کرد.فراخوانی
start()روی یک نمونهSharedMemoryManagerباعث شروع یک فرایند جدید میشود. تنها هدف این فرایند جدید، مدیریت چرخه عمر تمام بلوکهای حافظه مشترک ایجادشده از طریق آن است. برای آزادسازی تمام بلوکهای حافظه مشترک مدیریتشده توسط آن فرایند،shutdown()را روی این نمونه فراخوانی کنید. این کار باعث فراخوانیunlink()روی تمام اشیاءSharedMemoryمدیریتشده توسط آن فرایند میشود و سپس خود فرایند را متوقف میکند. با ایجاد نمونههایSharedMemoryاز طریق یکSharedMemoryManager، از نیاز به پیگیری دستی و آزادسازی منابع حافظه مشترک بینیاز میشویم.این کلاس متدهایی را برای ایجاد و برگرداندن نمونههای
SharedMemoryو برای ایجاد یک شیء فهرستمانند (ShareableList) مبتنی بر حافظه مشترک فراهم میکند.برای توضیح آرگومانهای ورودی اختیاری address و authkey بهارثرسیده و نحوهی استفاده از آنها برای اتصال به یک سرویس
SharedMemoryManagerموجود از فرایندهای دیگر، بهBaseManagerمراجعه کنید.- SharedMemory(size)¶
یک شیء
SharedMemoryجدید با size مشخصشده بر حسب بایت ایجاد میکند و بازمیگرداند.
- ShareableList(sequence)¶
یک شیء جدید
ShareableListرا ایجاد و برمیگرداند که با مقادیر sequence ورودی مقداردهی اولیه شده است.
مثال زیر سازوکارهای پایهی یک SharedMemoryManager را نشان میدهد:
>>> from multiprocessing.managers import SharedMemoryManager
>>> smm = SharedMemoryManager()
>>> smm.start() # Start the process that manages the shared memory blocks
>>> sl = smm.ShareableList(range(4))
>>> sl
ShareableList([0, 1, 2, 3], name='psm_6572_7512')
>>> raw_shm = smm.SharedMemory(size=128)
>>> another_sl = smm.ShareableList('alpha')
>>> another_sl
ShareableList(['a', 'l', 'p', 'h', 'a'], name='psm_6572_12221')
>>> smm.shutdown() # Calls unlink() on sl, raw_shm, and another_sl
مثال زیر الگویی احتمالاً راحتتر برای استفاده از اشیای SharedMemoryManager از طریق دستور with را نشان میدهد تا اطمینان حاصل شود که تمام بلوکهای حافظه مشترک پس از اینکه دیگر نیازی به آنها نیست، آزاد میشوند:
>>> with SharedMemoryManager() as smm:
... sl = smm.ShareableList(range(2000))
... # Divide the work among two processes, storing partial results in sl
... p1 = Process(target=do_work, args=(sl, 0, 1000))
... p2 = Process(target=do_work, args=(sl, 1000, 2000))
... p1.start()
... p2.start() # A multiprocessing.Pool might be more efficient
... p1.join()
... p2.join() # Wait for all work to complete in both processes
... total_result = sum(sl) # Consolidate the partial results now in sl
هنگامی که از یک SharedMemoryManager در یک دستور with استفاده میکنید، تمام بلوکهای حافظهی مشترک ایجادشده با استفاده از آن مدیر، زمانی که اجرای بلوک آپکد with به پایان میرسد، آزاد میشوند.
- class multiprocessing.shared_memory.ShareableList(sequence=None, *, name=None)¶
یک شیء فهرستمانند تغییرپذیر فراهم میکند که همه مقادیر ذخیرهشده در آن، در یک بلوک حافظه مشترک ذخیره میشوند. این موضوع مقادیر قابل ذخیره را به انواع داده توکار زیر محدود میکند:
int(۶۴بیتی علامتدار)str(هرکدام کمتر از ۱۰ مگابایت هنگام کدگذاری بهصورت UTF-8)bytes(هر کدام کمتر از ۱۰M بایت)None
همچنین بهطور قابلتوجهی با نوع
listتوکار تفاوت دارد، از این جهت که این فهرستها نمیتوانند طول کلی خود را تغییر دهند (یعنیappend()،insert()و غیره وجود ندارند) و از ایجاد پویای نمونههای جدیدShareableListاز طریق اسلایس پشتیبانی نمیکنند.sequence برای پر کردن یک
ShareableListجدید از مقادیر استفاده میشود. در عوض، برای اتصال به یکShareableListاز پیش موجود از طریق نام یکتای حافظه مشترک آن، آن را رویNoneتنظیم کنید.name نام یکتای حافظه مشترک درخواستشده است، همانطور که در تعریف
SharedMemoryتوضیح داده شده است. هنگام اتصال به یکShareableListموجود، نام یکتای بلوک حافظه مشترک آن را مشخص کنید، در حالی که sequence را رویNoneباقی میگذارید.توجه
یک مشکل شناختهشده برای مقادیر
bytesوstrوجود دارد. اگر آنها با بایتها یا نویسههای تهی (nul)\x00پایان یابند، ممکن است هنگام واکشی آنها با اندیس ازShareableList، بهصورت خاموش حذف شوند. این رفتار.rstrip(b'\x00')بهعنوان یک باگ در نظر گرفته میشود و ممکن است در آینده از بین برود. به gh-106939 مراجعه کنید.برای برنامههای کاربردی که حذف از راست (rstripping) نویسههای null انتهایی در آنها مشکلساز است، با افزودن همیشگی و بیقیدوشرط یک بایت اضافی غیرصفر به انتهای چنین مقادیری هنگام ذخیرهسازی و حذف بیقیدوشرط آن هنگام واکشی، این مشکل را دور بزنید:
>>> from multiprocessing import shared_memory >>> nul_bug_demo = shared_memory.ShareableList(['?\x00', b'\x03\x02\x01\x00\x00\x00']) >>> nul_bug_demo[0] '?' >>> nul_bug_demo[1] b'\x03\x02\x01' >>> nul_bug_demo.shm.unlink() >>> padded = shared_memory.ShareableList(['?\x00\x07', b'\x03\x02\x01\x00\x00\x00\x07']) >>> padded[0][:-1] '?\x00' >>> padded[1][:-1] b'\x03\x02\x01\x00\x00\x00' >>> padded.shm.unlink()
- count(value)¶
تعداد رخدادهای value را برمیگرداند.
- index(value)¶
اولین موقعیت اندیس value را برمیگرداند. اگر value موجود نباشد،
ValueErrorرا پرتاب میکند.
- format¶
ویژگی فقطخواندنی که شامل قالب بستهبندی
structاستفادهشده برای همهی مقادیر ذخیرهشدهی کنونی است.
- shm¶
نمونهی
SharedMemoryکه مقدارها در آن ذخیره شدهاند.
مثال زیر استفادهی مقدماتی از یک نمونهی ShareableList را نشان میدهد:
>>> from multiprocessing import shared_memory
>>> a = shared_memory.ShareableList(['howdy', b'HoWdY', -273.154, 100, None, True, 42])
>>> [ type(entry) for entry in a ]
[<class 'str'>, <class 'bytes'>, <class 'float'>, <class 'int'>, <class 'NoneType'>, <class 'bool'>, <class 'int'>]
>>> a[2]
-273.154
>>> a[2] = -78.5
>>> a[2]
-78.5
>>> a[2] = 'dry ice' # Changing data types is supported as well
>>> a[2]
'dry ice'
>>> a[2] = 'larger than previously allocated storage space'
Traceback (most recent call last):
...
ValueError: exceeds available storage for existing str
>>> a[2]
'dry ice'
>>> len(a)
7
>>> a.index(42)
6
>>> a.count(b'howdy')
0
>>> a.count(b'HoWdY')
1
>>> a.shm.close()
>>> a.shm.unlink()
>>> del a # Use of a ShareableList after call to unlink() is unsupported
مثال زیر نشان میدهد که چگونه یک، دو یا چند فرایند میتوانند با ارائه نام بلوک حافظه مشترک پشت آن، به همان ShareableList دسترسی یابند:
>>> b = shared_memory.ShareableList(range(5)) # In a first process
>>> c = shared_memory.ShareableList(name=b.shm.name) # In a second process
>>> c
ShareableList([0, 1, 2, 3, 4], name='...')
>>> c[-1] = -999
>>> b[-1]
-999
>>> b.shm.close()
>>> c.shm.close()
>>> c.shm.unlink()
مثالهای زیر نشان میدهند که اشیاء ShareableList (و SharedMemory زیربنایی) را میتوان در صورت نیاز پیکل و پیکلگشایی (unpickle) کرد. توجه داشته باشید که این شیء همچنان همان شیء مشترک خواهد بود. این اتفاق به این دلیل رخ میدهد که شیء پیکلگشاییشده دارای همان نام یکتا است و صرفاً به یک شیء موجود با همان نام متصل میشود (اگر شیء هنوز زنده باشد):
>>> import pickle
>>> from multiprocessing import shared_memory
>>> sl = shared_memory.ShareableList(range(10))
>>> list(sl)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> deserialized_sl = pickle.loads(pickle.dumps(sl))
>>> list(deserialized_sl)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> sl[0] = -1
>>> deserialized_sl[1] = -2
>>> list(sl)
[-1, -2, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(deserialized_sl)
[-1, -2, 2, 3, 4, 5, 6, 7, 8, 9]
>>> sl.shm.close()
>>> sl.shm.unlink()