Python & Raspberry Pi 開発ブログ ☆彡

Raspberry Pi 3の使い方、設定をわかりやすく解説。Raspberry Pi3 Model B(Element14版)、Raspbianを使用して開発中。

【スポンサーリンク】

multiprocessingの共有メモリの使い方

前回、multiprocessingモジュールで並列処理をする方法を紹介しました。multiprocessingモジュールではプロセスが分かれるため、プロセス間で変数のやり取りをするには少し工夫が必要です。工夫と言っても、それほど難しくはありません。Managerクラスを使うことによって、共有メモリを使うことができるようになります。

Managerクラスの使い方

Managerクラスを使って、変数を生成するとプロセス間で変数を共有できるようになります。Value型、Array型は少し使い方に特徴があるので説明しておきます。

Value型の変数の使い方

m_val = Value('i',1) # 'i' は符号付整数の単一の変数を生成、初期値:1
m_val.value = 1 # 実際に値を書いたり読んだりする場合は、変数名.valueと書く必要があります。

Array型の変数の使い方

m_array = Array('d',range(2)) # 'd'は倍精度浮動小数の配列変数を定義 配列数:2
m_array[0] = 1

list、dict、queue型はいつも通り使えますが、私が試したところ、list型はm_list[0] = 1という書き方だとうまく動かないようです。Array型で同じ書き方ができるので、そちらを使ってください。

サンプルコードの紹介

サンプルコードは以下の動作をします。

  1. MyProcess1とMyProcess2を別プロセスで生成
  2. MyProcess1で共有メモリの値をカウントアップ
  3. MyProcess2で共有メモリの値を読む

プロセス間で変数が共有されていれば、MyProcess1でカウントアップした値がMyProcess2で表示されるはずです。

from concurrent.futures import ProcessPoolExecutor
from multiprocessing import Manager
import time
from datetime import datetime


class MyProcess1:

    def __init__(self, m_val, m_array, m_list, m_dict, m_queue):
        self.m_val = m_val
        self.m_array = m_array
        self.m_list = m_list
        self.m_dict = m_dict
        self.m_queue = m_queue

    def run(self):
        # 上記変数に値を書き込む        
        cnt = 0
        while True:
            print('[{}] : {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'MyProcess1:ValueWrite') )
            self.m_val.value = cnt
            self.m_array[0] = cnt
            self.m_list.append(cnt)
            self.m_dict['val'] = cnt
            self.m_queue.put(cnt)
            cnt = cnt + 1            
            time.sleep(1)

class MyProcess2:

    def __init__(self, m_val, m_array, m_list, m_dict, m_queue):
        self.m_val = m_val
        self.m_array = m_array
        self.m_list = m_list
        self.m_dict = m_dict
        self.m_queue = m_queue

    def run(self):
        # 上記変数の値を読む
        while True:
            q_data  = self.m_queue.get()
            print('[{}] : {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'MyProcess2:ValueRead') )
            print('[{}] : Value {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), self.m_val.value ) )
            print('[{}] : Array {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), self.m_array[0] ) )
            print('[{}] : list {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), self.m_list[:] ) )
            print('[{}] : dict {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), self.m_dict ) )
            print('[{}] : Queue {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), q_data) )
            
if __name__ == "__main__":
    
    manager =  Manager()
    
    m_val = manager.Value('i', 0) # プロセス間共有できるValue型変数を作成
    m_array = manager.Array('d', range(10)) # プロセス間共有できるArray型(配列)変数を作成
    m_list = manager.list() # プロセス間共有できるList型変数を作成
    m_dict = manager.dict() # プロセス間共有できる辞書型変数を作成
    m_queue = manager.Queue() # プロセス間共有できるキュー型変数を作成
    process_pool = [] # プロセス生成したい関数を登録
    process_pool.append(MyProcess1(m_val, m_array, m_list, m_dict, m_queue).run) # 引数に上記で作成した変数を与える
    process_pool.append(MyProcess2(m_val, m_array, m_list, m_dict, m_queue).run) # 引数に上記で作成した変数を与える
    with ProcessPoolExecutor(max_workers=2) as executor: # max_workerは同時に動かすプロセスの最大数 Noneを指定するとコア数 * 4の値になる
        for process in process_pool:
            results = executor.submit(process) # submit関数で、list内の関数をプロセス生成
        for result in results:
            print(result)

動作確認

MyProcess1クラスでセットした値が表示されていると思います。表示しているのは、MyProcess2クラスですので、MyProcess1とMyProcess2の異なるプロセス間で変数が共有されていることがわかります。

[2019-12-11 19:16:21] : MyProcess1:ValueWrite
[2019-12-11 19:16:21] : MyProcess2:ValueRead
[2019-12-11 19:16:21] : Value 0
[2019-12-11 19:16:21] : Array 0.0
[2019-12-11 19:16:21] : list [0]
[2019-12-11 19:16:21] : dict {'val': 0}
[2019-12-11 19:16:21] : Queue 0
[2019-12-11 19:16:22] : MyProcess1:ValueWrite
[2019-12-11 19:16:22] : MyProcess2:ValueRead
[2019-12-11 19:16:22] : Value 1
[2019-12-11 19:16:22] : Array 1.0
[2019-12-11 19:16:22] : list [0, 1]
[2019-12-11 19:16:22] : dict {'val': 1}
[2019-12-11 19:16:22] : Queue 1
[2019-12-11 19:16:23] : MyProcess1:ValueWrite
[2019-12-11 19:16:23] : MyProcess2:ValueRead
[2019-12-11 19:16:23] : Value 2
[2019-12-11 19:16:23] : Array 2.0
[2019-12-11 19:16:23] : list [0, 1, 2]
[2019-12-11 19:16:23] : dict {'val': 2}
[2019-12-11 19:16:23] : Queue 2
[2019-12-11 19:16:24] : MyProcess1:ValueWrite
[2019-12-11 19:16:24] : MyProcess2:ValueRead
[2019-12-11 19:16:24] : Value 3
[2019-12-11 19:16:24] : Array 3.0
[2019-12-11 19:16:24] : list [0, 1, 2, 3]
[2019-12-11 19:16:24] : dict {'val': 3}
[2019-12-11 19:16:24] : Queue 3

これでマルチプロセスも恐れずに使えますね。マルチスレッドよりも高速で処理ができるようになります。ただ、Raspberry Piはそこそこマシンパワーがあるので、たいていはマルチスレッドで十分かもしれません。

ラズパイ ソフトウェアに戻る
トップページに戻る