4BでmultiprocessingのManagerを使って、ndarrayを共有する

Windowsの再インストールというトラブルに合い、現在Pythonはラズベリーパイだけでやっています。ただコードはWindows10のメモ帳で書いています。WinSCPでラズベリーパイへファイル転送し、VNCviewerでラズベリーパイ上のThonnyを起動しています（ラズベリーパイは小モニターを付けて、棚の上に置いています）。WinSCP・VNCviewerは、IPアドレス入力だけでつながります。暗号化・認証でもエラーは出たことがありません。ラズベリーパイOSのBuster・Bookwormと大変相性がいいです。ラズベリーパイのPythonコードを作成する上で、'三種の神器'です。

開発環境の話が少し長くなりましたが、今回はラズベリーパイ4B上で、OSのbookwormとbusterを使いました。タイトルにあるPythonのMultiprocessingのManegerのメモリー共有は、「① 前設定が簡単である」、「② listが使える」という利点があります。ただManagerは「処理速度が少し遅い」という欠点があります。またmultiprocessingのメモリー共有には、Manager以外にctypesも含め多くの高速な方法があります。しかし今回は、Managerが「① 前設定が簡単」であることに着目しました。というより初心者なので、他の方法は少し難解でした。そしてndarrayを共有する上で、「② listが使える」ことも魅力でした。

コードは、最初にcore1で、4個の画像ファイルを読み込みndarrayとします。さらに同じcore1で、画像ndarrayを「Managerのlist」に書き込みます。次にcore4で、この「Managerのlist」を読み出し、画像ndarrayに戻します。最後に同じcore4で、画像ndarrayをディスプレイに表示します。これのループです。

またコード作成の途上で意外だったのは、「Managerのlist」の「読み出しエラー」が多いことです。ファンを回して冷却すると、エラー率は少し下がります。しかしファンで冷却しても、「読み出しエラー」はまだ残ります。今まで、「pythonの標準のlist」操作でエラーは無かったので意外でした。ほとんどの部分が、「Managerのlist」の「読み出しエラー」のクリアです。以下がコードです。

```
# manager-ndarray---bookworm-buster.py
import time
from multiprocessing import Manager, Value, Process
import numpy as np
import cv2 as cv


# Raspberry Pi 4B, 8G   under cooling   on Thonny
# ------------------------------------------------
# Buster(x32) OS:  Linux raspberrypi 5.10.103-v7l+ #1529 SMP Tue Mar 8 12:24:00 GMT 2022 armv7l GNU/Linux
# Python 3.7.3 (default, Oct 31 2022, 14:04:00)
# numpy  1.20.1
# OpenCV 3.4.15.55
# ------------------------------------------------
# Bookworm(x64) OS:  Linux raspberrypi 6.1.0-rpi7-rpi-v8 #1 SMP PREEMPT Debian 1:6.1.63-1+rpt1 (2023-11-24) aarch64 GNU/Linux
# Python 3.11.2 (main, Mar 13 2023, 12:18:29)
# numpy  1.24.2
# OpenCV  4.6.0  (sudo apt install python3-opencv)
# ------------------------------------------------


def process1(vcount00, f_plist0, plist0):

    img00 = cv.imread("d00.png")   #640w x 480h
    img01 = cv.imread("d01.png")
    img02 = cv.imread("d02.png")
    img03 = cv.imread("d03.png")   #warning for bookworm

    height00 = 480
    width00  = 640
    rgb00    =   3

    try:
        while(True):
            count00_old = vcount00.value
            q,  mod = divmod(count00_old, 4)
            if   mod  == 0:
                img480 = img00.copy()
            elif mod == 1:
                img480 = img01.copy()
            elif mod == 2:
                img480 = img02.copy()
            elif mod == 3:
                img480 = img03.copy()

            ## preWriting 3D-->4D
            #print(repr(img480))
            img480_4d  = np.reshape(img480, [1, height00, width00, rgb00])
            #print(repr(img480_4d))
            
            ## Writing to plist0
            while True:
                time.sleep(0.0001)
                if f_plist0.value == 0:
                    f_plist0.value = 9
                if f_plist0.value == 9 or f_plist0.value == 1:
                    f_plist0.value = 1
                    break
            plist0[:]= []
            for i in img480_4d:
                plist0.append(i)
            if len(plist0)==0:
                print("plist0==0") #### No error
            f_plist0.value = 9
            vcount00.value  += 1
            time.sleep(0.5)

    except KeyboardInterrupt:
        print('process1  excepted')


def process4(vcount00, f_plist0, plist0):

    img = cv.imread("Init.png")   #warning for bookworm
    cv.imshow("Image1", img)
    key = cv.waitKey(1000)

    height00 = 480 
    width00  = 640
    rgb00   =   3

    # a_ndarray00c  = np.reshape(img, [1, height00, width00, rgb00])

    count0_old=0
    try:
        while(True):
            l_ndarray00=[]
            time.sleep(0.001)
            if count0_old < vcount00.value :
                count0_old = vcount00.value

                ## Reading from plist0
                i_retry = 0
                while True: 

                    while True:
                        time.sleep(0.0001)
                        if f_plist0.value == 9:    
                            f_plist0.value = 4
                            break
                    for i in plist0:
                        l_ndarray00.append(i)
                    f_plist0.value = 9

                    # double 'while' loop for read-error
                    i_retry += 1
                    if i_retry >= 20:
                        print("i_retry >= 20  error")
                        break
                    if len(l_ndarray00) == 0:
                        time.sleep(0.0001)
                        continue
                    else:
                        print(i_retry)    #### sometimes out of 1
                        a_ndarray00 = np.array(l_ndarray00, dtype="uint8")
                        a_ndarray00c = a_ndarray00.copy()
                        break

                # 4D-->3D
                a_ndarray00c333  = np.reshape(a_ndarray00c, [height00, width00, rgb00])
                img480 = cv.resize(a_ndarray00c333, (width00, height00))

                ##  imshow
                cv.imshow("Image1", img480)
                key = cv.waitKey(10)

    except KeyboardInterrupt:
        cv.destroyAllWindows()
        print('process4  excepted')


if __name__ == "__main__":

    with Manager() as manager:

        vcount00 = manager.Value('i', 0)
        vcount00.value  = 0

        f_plist0 = manager.Value('i', 0)
        f_plist0.value  = 1

        plist0  =  manager.list()

        process1 = Process(target=process1, args=[vcount00, f_plist0, plist0])
        process4 = Process(target=process4, args=[vcount00, f_plist0, plist0])

        process1.start()
        process4.start()

        process1.join()
        process4.join()

```

動かしてみて、bookworm(x64)とBuster(x32)は、ほぼ同じ動作でした。ただ画面操作などをすると、時々1以外が出ます。割り込みが、同じcoreを使っているのかもしれません。動作上は、今回のシンプルなエラー回避策により、「Managerのlist」で、共有できることがわかりました。

お忙しい中、ご覧いただきありがとうございました。
なお公開プログラムは、コピー・改変も自由です。著作権に関する問題も発生しません。

本ページは、Pythonのmultiprocessing 等を参考にしています。
https://docs.python.org/ja/3.7/library/multiprocessing.html
https://numpy.org/doc/1.16/reference/generated/numpy.array.html
https://numpy.org/doc/1.16/reference/generated/numpy.reshape.html

以下はプログラム中で使用した画像です。
![Init.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675573/2f98bddf-3451-879c-71d9-733c071e81de.png)
Init.png
![d00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675573/0fe681b7-d582-deaf-116a-52357edf0e08.png)
d00.png
![d01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675573/e5af5d5b-480b-f784-192b-6a26f19cfba1.png)
d01.png
![d02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675573/b6fe0b33-8593-cab2-7093-15ec539f0c9b.png)
d02.png
![d03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675573/36373b5c-aee7-632e-57d2-27aae7be40e7.png)
d03.png