multiprocessing.shared_memory --- Bộ nhớ dùng chung để truy cập trực tiếp qua các tiến trình

Source code: Lib/multiprocessing/shared_memory.py

Added in version 3.8.


Mô-đun này cung cấp một lớp, SharedMemory, để phân bổ và quản lý bộ nhớ dùng chung được truy cập bởi một hoặc nhiều quy trình trên máy đa bộ xử lý đối xứng hoặc đa lõi (SMP). Để hỗ trợ quản lý vòng đời của bộ nhớ dùng chung, đặc biệt là trên các quy trình riêng biệt, một lớp con BaseManager, SharedMemoryManager, cũng được cung cấp trong mô-đun multiprocessing.managers.

Trong mô-đun này, bộ nhớ dùng chung đề cập đến các khối bộ nhớ dùng chung "kiểu POSIX" (mặc dù không nhất thiết phải được triển khai rõ ràng như vậy) và không đề cập đến "bộ nhớ chia sẻ được phân phối". Kiểu bộ nhớ dùng chung này cho phép các tiến trình riêng biệt có khả năng đọc và ghi vào vùng chung (hoặc dùng chung) của bộ nhớ khả biến. Các quy trình thường bị giới hạn ở mức chỉ có quyền truy cập vào không gian bộ nhớ quy trình của riêng chúng nhưng bộ nhớ dùng chung cho phép chia sẻ dữ liệu giữa các quy trình, tránh nhu cầu gửi tin nhắn giữa các quy trình chứa dữ liệu đó. Chia sẻ dữ liệu trực tiếp qua bộ nhớ có thể mang lại lợi ích hiệu suất đáng kể so với việc chia sẻ dữ liệu qua đĩa hoặc ổ cắm hoặc các phương thức liên lạc khác yêu cầu tuần tự hóa/giải tuần tự hóa và sao chép dữ liệu.

class multiprocessing.shared_memory.SharedMemory(name=None, create=False, size=0, *, track=True)

Tạo một thể hiện của lớp SharedMemory để tạo khối bộ nhớ dùng chung mới hoặc gắn vào khối bộ nhớ dùng chung hiện có. Mỗi khối bộ nhớ dùng chung được gán một tên duy nhất. Bằng cách này, một tiến trình có thể tạo một khối bộ nhớ dùng chung với một tên cụ thể và một tiến trình khác có thể gắn vào cùng khối bộ nhớ dùng chung đó bằng cùng tên đó.

Là tài nguyên để chia sẻ dữ liệu giữa các quy trình, các khối bộ nhớ dùng chung có thể tồn tại lâu hơn quy trình ban đầu đã tạo ra chúng. Khi một tiến trình không còn cần quyền truy cập vào khối bộ nhớ dùng chung mà các tiến trình khác có thể vẫn cần, thì phương thức close() sẽ được gọi. Khi bất kỳ tiến trình nào không còn cần khối bộ nhớ dùng chung nữa, phương thức unlink() sẽ được gọi để đảm bảo việc dọn dẹp thích hợp.

Tham số:
  • name (str | None) -- Tên duy nhất cho bộ nhớ dùng chung được yêu cầu, được chỉ định dưới dạng chuỗi. Khi tạo khối bộ nhớ dùng chung mới, nếu None (mặc định) được cung cấp cho tên thì tên mới sẽ được tạo.

  • create (bool) -- Kiểm soát xem khối bộ nhớ chia sẻ mới được tạo (True) hay khối bộ nhớ chia sẻ hiện có được đính kèm (False).

  • size (int) -- Số byte được yêu cầu khi tạo khối bộ nhớ dùng chung mới. Vì một số nền tảng chọn phân bổ các khối bộ nhớ dựa trên kích thước trang bộ nhớ của nền tảng đó nên kích thước chính xác của khối bộ nhớ dùng chung có thể lớn hơn hoặc bằng kích thước được yêu cầu. Khi gắn vào khối bộ nhớ dùng chung hiện có, tham số size sẽ bị bỏ qua.

  • track (bool) -- Khi True, hãy đăng ký khối bộ nhớ dùng chung với quy trình theo dõi tài nguyên trên các nền tảng mà hệ điều hành không tự động thực hiện việc này. Trình theo dõi tài nguyên đảm bảo dọn dẹp đúng cách bộ nhớ dùng chung ngay cả khi tất cả các tiến trình khác có quyền truy cập vào bộ nhớ đều thoát ra mà không làm như vậy. Các quy trình Python được tạo từ một tổ tiên chung sử dụng cơ sở multiprocessing chia sẻ một quy trình theo dõi tài nguyên duy nhất và thời gian tồn tại của các phân đoạn bộ nhớ dùng chung được xử lý tự động giữa các quy trình này. Các quy trình Python được tạo theo bất kỳ cách nào khác sẽ nhận được trình theo dõi tài nguyên của riêng chúng khi truy cập bộ nhớ dùng chung có bật track. Điều này sẽ khiến bộ nhớ dùng chung bị xóa bởi trình theo dõi tài nguyên của quá trình đầu tiên kết thúc. Để tránh sự cố này, người dùng subprocess hoặc các quy trình Python độc lập nên đặt track thành False khi đã có sẵn một quy trình khác thực hiện việc ghi sổ. track bị bỏ qua trên Windows, Windows có tính năng theo dõi riêng và tự động xóa bộ nhớ dùng chung khi tất cả các thẻ điều khiển đối với nó đã bị đóng.

Thay đổi trong phiên bản 3.13: Đã thêm tham số track.

close()

Đóng bộ mô tả/xử lý tệp vào bộ nhớ dùng chung từ phiên bản này. close() sẽ được gọi khi quyền truy cập vào khối bộ nhớ dùng chung từ phiên bản này không còn cần thiết nữa. Tùy thuộc vào hệ điều hành, bộ nhớ cơ bản có thể được giải phóng hoặc không ngay cả khi tất cả các thẻ điều khiển đối với nó đã bị đóng. Để đảm bảo dọn dẹp đúng cách, hãy sử dụng phương pháp unlink().

Xóa khối bộ nhớ chia sẻ cơ bản. Điều này chỉ nên được gọi một lần trên mỗi khối bộ nhớ dùng chung bất kể số lượng thẻ điều khiển đối với nó, ngay cả trong các quy trình khác. unlink()close() có thể được gọi theo bất kỳ thứ tự nào, nhưng việc cố gắng truy cập dữ liệu bên trong khối bộ nhớ dùng chung sau unlink() có thể dẫn đến lỗi truy cập bộ nhớ, tùy thuộc vào nền tảng.

Phương pháp này không có tác dụng trên Windows, trong đó cách duy nhất để xóa khối bộ nhớ dùng chung là đóng tất cả các thẻ điều khiển.

buf

Chế độ xem bộ nhớ về nội dung của khối bộ nhớ dùng chung.

name

Quyền truy cập chỉ đọc vào tên duy nhất của khối bộ nhớ dùng chung.

size

Quyền truy cập chỉ đọc đối với kích thước tính bằng byte của khối bộ nhớ dùng chung.

Ví dụ sau đây minh họa việc sử dụng phiên bản SharedMemory ở mức độ thấp:

>>> từ nhập đa xử  Shared_memory
>>> shm_a = Shared_memory.SharedMemory(create=True, size=10)
>>> (shm_a.buf)
<lớp 'bộ nhớ'>
>>> bộ đệm = shm_a.buf
>>> len(bộ đệm)
10
>>> buffer[:4] = bytearray([22, 33, 44, 55]) # Modify nhiều lần cùng một lúc
>>> bộ đệm[4] = 100 # Modify một byte mỗi lần
>>> # Attach vào khối bộ nhớ dùng chung hiện có
>>> shm_b = Shared_memory.SharedMemory(shm_a.name)
>>> nhập mảng
>>> array.array('b', shm_b.buf[:5]) # Copy dữ liệu vào một array.array mới
mảng('b', [22, 33, 44, 55, 100])
>>> shm_b.buf[:5] = b'howdy' # Modify qua shm_b sử dụng byte
>>> byte(shm_a.buf[:5]) # Access qua shm_a
xin chào'
>>> shm_b.close() # Close mỗi phiên bản SharedMemory
>>> shm_a.close()
>>> shm_a.unlink() # Call chỉ hủy liên kết một lần để giải phóng bộ nhớ dùng chung

Ví dụ sau đây minh họa cách sử dụng thực tế của lớp SharedMemory với NumPy arrays, truy cập cùng một numpy.ndarray từ hai shell Python riêng biệt:

>>> # In shell tương tác Python đầu tiên
>>> nhập numpy dưới dạng np
>>> a = np.array([1, 1, 2, 3, 5, 8]) # Start với mảng NumPy hiện có
>>> từ nhập đa xử  Shared_memory
>>> shm = Shared_memory.SharedMemory(create=True, size=a.nbytes)
>>> # Now tạo mảng NumPy được hỗ trợ bởi bộ nhớ dùng chung
>>> b = np.ndarray(a.shape, dtype=a.dtype, buffer=shm.buf)
>>> b[:] = a[:] # Copy dữ liệu gốc vào bộ nhớ dùng chung
>>>b
mảng([1, 1, 2, 3, 5, 8])
>>> loại (b)
<lớp 'numpy.ndarray'>
>>> loại (a)
<lớp 'numpy.ndarray'>
>>> shm.name # We không chỉ định tên nên một cái đã được chọn cho chúng tôi
'psm_21467_46075'

>>> # In cùng một shell hoặc một shell Python mới trên cùng một máy
>>> nhập numpy dưới dạng np
>>> từ nhập đa xử  Shared_memory
>>> # Attach vào khối bộ nhớ dùng chung hiện có
>>> hiện có_shm = Shared_memory.SharedMemory(name='psm_21467_46075')
>>> # Note rằng a.shape là (6,) và a.dtype là np.int64 trong ví dụ này
>>> c = np.ndarray((6,), dtype=np.int64, buffer=being_shm.buf)
>>> c
mảng([1, 1, 2, 3, 5, 8])
>>> c[-1] = 888
>>> c
mảng([ 1, 1, 2, 3, 5, 888])

>>> # Back trong shell tương tác Python đầu tiên, b phản ánh sự thay đổi này
>>>b
mảng([ 1, 1, 2, 3, 5, 888])

>>> # Clean từ bên trong shell Python thứ hai
>>> del c # Unnecessary; chỉ nhấn mạnh mảng không còn được sử dụng
>>> hiện_shm.close()

>>> # Clean từ bên trong shell Python đầu tiên
>>> del b # Unnecessary; chỉ nhấn mạnh mảng không còn được sử dụng
>>> shm.close()
>>> shm.unlink() # Free và giải phóng khối bộ nhớ dùng chung ở cuối
class multiprocessing.managers.SharedMemoryManager([address[, authkey]])

Một lớp con của multiprocessing.managers.BaseManager có thể được sử dụng để quản lý các khối bộ nhớ dùng chung giữa các tiến trình.

Lệnh gọi tới start() trên phiên bản SharedMemoryManager sẽ khiến một quy trình mới được bắt đầu. Mục đích duy nhất của quy trình mới này là quản lý vòng đời của tất cả các khối bộ nhớ dùng chung được tạo thông qua nó. Để kích hoạt giải phóng tất cả các khối bộ nhớ dùng chung do quy trình đó quản lý, hãy gọi shutdown() trên phiên bản. Điều này kích hoạt lệnh gọi unlink() trên tất cả các đối tượng SharedMemory được quản lý bởi quy trình đó và sau đó tự dừng quá trình đó. Bằng cách tạo các phiên bản SharedMemory thông qua SharedMemoryManager, chúng tôi tránh được việc phải theo dõi và kích hoạt giải phóng tài nguyên bộ nhớ dùng chung theo cách thủ công.

Lớp này cung cấp các phương thức để tạo và trả về các phiên bản SharedMemory cũng như để tạo một đối tượng giống như danh sách (ShareableList) được hỗ trợ bởi bộ nhớ dùng chung.

Tham khảo BaseManager để biết mô tả về các đối số đầu vào tùy chọn addressauthkey được kế thừa cũng như cách chúng có thể được sử dụng để kết nối với dịch vụ SharedMemoryManager hiện có từ các quy trình khác.

SharedMemory(size)

Tạo và trả về một đối tượng SharedMemory mới với size được chỉ định theo byte.

ShareableList(sequence)

Tạo và trả về một đối tượng ShareableList mới, được khởi tạo bởi các giá trị từ sequence đầu vào.

Ví dụ sau đây minh họa các cơ chế cơ bản của SharedMemoryManager:

>>> từ multiprocessing.managers nhập SharedMemoryManager
>>> smm = SharedMemoryManager()
>>> smm.start() # Start quy trình quản lý các khối bộ nhớ dùng chung
>>> 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() trên sl, raw_shm và another_sl

Ví dụ sau mô tả một mẫu có khả năng thuận tiện hơn khi sử dụng các đối tượng SharedMemoryManager thông qua câu lệnh with để đảm bảo rằng tất cả các khối bộ nhớ dùng chung được giải phóng sau khi chúng không còn cần thiết nữa:

>>> với SharedMemoryManager()  smm:
... sl = smm.ShareableList(range(2000))
... # Divide công việc giữa hai quy trình, lưu trữ một phần kết quả trong sl
... p1 = Quá trình(target=do_work, args=(sl, 0, 1000))
... p2 = Quá trình(target=do_work, args=(sl, 1000, 2000))
... p1.start()
... p2.start() # A multiprocessing.Pool có thể hiệu quả hơn
... p1.join()
... p2.join() # Wait để hoàn thành mọi công việc trong cả hai quy trình
... Total_result = sum(sl) # Consolidate một phần kết quả bây giờ ở dạng sl

Khi sử dụng SharedMemoryManager trong câu lệnh with, các khối bộ nhớ dùng chung được tạo bằng trình quản lý đó đều được giải phóng khi khối mã của câu lệnh with kết thúc thực thi.

class multiprocessing.shared_memory.ShareableList(sequence=None, *, name=None)

Cung cấp một đối tượng giống như danh sách có thể thay đổi trong đó tất cả các giá trị được lưu trữ bên trong đều được lưu trữ trong một khối bộ nhớ dùng chung. Điều này hạn chế các giá trị có thể lưu trữ đối với các kiểu dữ liệu tích hợp sau:

  • int (có chữ ký 64-bit)

  • float

  • bool

  • str (mỗi byte nhỏ hơn 10M byte khi được mã hóa thành UTF-8)

  • bytes (mỗi cái nhỏ hơn 10M byte)

  • None

Nó cũng khác biệt đáng kể so với loại list tích hợp ở chỗ các danh sách này không thể thay đổi độ dài tổng thể của chúng (tức là không có append(), insert(), v.v.) và không hỗ trợ việc tạo động các phiên bản ShareableList mới thông qua việc cắt.

sequence được sử dụng để điền vào một ShareableList mới chứa đầy các giá trị. Đặt thành None để gắn vào ShareableList hiện có bằng tên bộ nhớ dùng chung duy nhất của nó.

name là tên duy nhất cho bộ nhớ dùng chung được yêu cầu, như được mô tả trong định nghĩa cho SharedMemory. Khi gắn vào ShareableList hiện có, hãy chỉ định tên duy nhất của khối bộ nhớ dùng chung trong khi để sequence được đặt thành None.

Ghi chú

Đã xảy ra sự cố đã biết đối với các giá trị bytesstr. Nếu chúng kết thúc bằng \x00 nul byte hoặc ký tự, thì chúng có thể là silently stripped khi tìm nạp chúng theo chỉ mục từ ShareableList. Hành vi .rstrip(b'\x00') này được coi là một lỗi và có thể biến mất trong tương lai. Xem gh-106939.

Đối với các ứng dụng trong đó việc loại bỏ các giá trị null ở cuối là một vấn đề, hãy giải quyết vấn đề đó bằng cách luôn thêm một byte khác 0 vào cuối các giá trị đó một cách vô điều kiện khi lưu trữ và loại bỏ nó một cách vô điều kiện khi tìm nạp:

>>> từ nhập đa xử  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()
>>> độn = Shared_memory.ShareableList(['?\x00\x07', b'\x03\x02\x01\x00\x00\x00\x07'])
>>> đệm[0][:-1]
'?\x00'
>>> độn[1][:-1]
b'\x03\x02\x01\x00\x00\x00'
>>> độn.shm.unlink()
count(value)

Trả về số lần xuất hiện của value.

index(value)

Trả về vị trí chỉ mục đầu tiên của value. Tăng ValueError nếu value không có mặt.

format

Thuộc tính chỉ đọc chứa định dạng đóng gói struct được sử dụng bởi tất cả các giá trị hiện được lưu trữ.

shm

Phiên bản SharedMemory nơi các giá trị được lưu trữ.

Ví dụ sau đây minh họa cách sử dụng cơ bản của phiên bản 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

Ví dụ sau mô tả cách một, hai hoặc nhiều tiến trình có thể truy cập vào cùng một ShareableList bằng cách cung cấp tên của khối bộ nhớ dùng chung đằng sau nó:

>>> 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()

Các ví dụ sau đây chứng minh rằng các đối tượng ShareableList (và SharedMemory cơ bản) có thể được chọn và không được chọn nếu cần. Lưu ý rằng nó vẫn sẽ là đối tượng được chia sẻ tương tự. Điều này xảy ra vì đối tượng được deserialized có cùng tên duy nhất và chỉ được gắn vào một đối tượng hiện có có cùng tên (nếu đối tượng vẫn còn tồn tại):

>>> 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()