ブログ管理者のP.Hです!
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型で同じ書き方ができるので、そちらを使ってください。
サンプルコードの紹介
サンプルコードは以下の動作をします。
- MyProcess1とMyProcess2を別プロセスで生成
- MyProcess1で共有メモリの値をカウントアップ
- 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はそこそこマシンパワーがあるので、たいていはマルチスレッドで十分かもしれません。