並列処理
前回、どこからでもアクセス可能な変数であるグローバル変数を紹介しました。
今回は並列処理により複数の処理を同時に行い、グローバル変数を使って一つの処理の終了により他の処理も終了させる方法を紹介します。
どういうことかと言うと、例えば処理1、処理2という2つの処理を並列に行っていたとします。
通常であれば処理1が終わっても、処理2は独自に動いており、その処理が終わるまでプログラムは終了しません。
しかし時によっては処理1が終了したら処理2も終了させたいという場面もあることでしょう。
具体例は最後に紹介しますが、そういった時に役立つ方法を今回は紹介します。
それでは始めていきましょう。
2つの関数を並列処理
まずは並列処理のおさらいからです。
例えば今回はこのように特定の時間待つtimeWait関数と1秒ごとにランダムな数字を表示するrandomValue関数があったとします。
def timeWait():
i = 0
while i <= waittime:
print(f'timeWait: {i} sec' )
time.sleep(1)
i = i + 1
def randomValue():
while True:
print(f'randomValue: {random.randrange(0,10)} ')
time.sleep(1)
どちらもwhile文を使って繰り返しされていますが、timeWait関数では「while i <= waittime:」ということで変数waittimeで指定された値まで繰り返されます。
randomValue関数では「while True:」なので永遠と繰り返されます。
この2つの処理をconcurrent.futuresのThreadPoolExecutorを使って並列処理化してみましょう。
ちなみにThreadPoolExecutorに関してはこちらの記事で紹介していますのでよかったらどうぞ。
並列化したプログラムはこんな感じです。
import time
import random
from concurrent.futures import ThreadPoolExecutor
waittime = 5
def main():
with ThreadPoolExecutor(max_workers=2) as executor:
executor.submit(timeWait)
executor.submit(randomValue)
def timeWait():
i = 0
while i <= waittime:
print(f'timeWait: {i} sec ')
time.sleep(1)
i = i + 1
def randomValue():
while True:
print(f'randomValue: {random.randrange(0,10)} ')
time.sleep(1)
if __name__ == '__main__':
main()
実行結果
timeWait: 0 sec randomValue: 9
randomValue: 6 timeWait: 1 sec
timeWait: 2 sec randomValue: 9
randomValue: 5 timeWait: 3 sec
timeWait: 4 sec
randomValue: 3
timeWait: 5 sec
randomValue: 0
randomValue: 2
randomValue: 8
randomValue: 6
randomValue: 5
randomValue: 8
実行結果を見てみると、最初の方はtimeWait関数での表示とrandomValue関数での表示が同時に出力されています。
ただし途中からrandomValue関数の結果だけになっています。
これはtimeWait関数の処理が終わったのですが、randomValue関数は終了がないため、処理が続いていると言う状態です。
今回はこのtimeWait関数の処理が終わったら、randomValue関数の処理が終わるようにしたいと思います。
並列処理で特定の処理の終了を受け取り、処理を終了させる方法
ということで並列処理した際、特定の処理の終了を受け取り、他の処理を終了させる方法です。
これは前回勉強したグローバル変数を用います。
プログラム全体としてはこんな感じです。
import time
import random
from concurrent.futures import ThreadPoolExecutor
waittime = 5
flag = True
def main():
with ThreadPoolExecutor(max_workers=2) as executor:
executor.submit(timeWait)
executor.submit(randomValue)
def timeWait():
i = 0
global flag
while i <= waittime:
print(f'timeWait: {i} sec ')
time.sleep(1)
i = i + 1
flag = False
def randomValue():
global flag
while flag == True:
print(f'randomValue: {random.randrange(0,10)} ')
time.sleep(1)
if __name__ == '__main__':
main()
実行結果
timeWait: 0 sec
randomValue: 2
timeWait: 1 sec randomValue: 5
timeWait: 2 sec randomValue: 6
randomValue: 2
timeWait: 3 sec
randomValue: 1 timeWait: 4 sec
timeWait: 5 sec
randomValue: 8
グローバル変数として「flag」が定義されており、値は「True」とされています(flag = True)。
このグローバル変数をtimeWait関数内でもrandomValue関数内でも定義します(global flag)。
そしてtimeWait関数内では最後に「flag = False」を追加し、処理が全て終了したら、flagをFalseに書き換えるようにします。
randomValue関数では「while True:」を「while flag == True:」とすることで、flagがTrueの間だけ繰り返すように変更します。
こうすることでtimeWait関数が処理を終え、flagがFalseとなると、そのシグナルをrandomValue関数で受け、同じく処理を終了するというわけです。
使用例
具体的にどういった時に使うのかと言うと、例えば前にPyAutoGUIというライブラリを使い、マウスやキーボード操作を自動化しようというお話をしました。
この時はPythonでの処理が長くなり、その間マウスもキーボードも触らず放置するとPCがスリープしてしまうという問題があり、その対策の一つとしてPyAutoGUIで操作を自動化するということでした。
つまり今回の並列処理+グローバル変数と組み合わせることで、特定の処理をさせている間、マウスやキーボードを自動で操作し、スリープしないようにするということが可能になるわけです。
そこでこんな感じのプログラムを組んでみました。
from concurrent.futures import ThreadPoolExecutor
import pyautogui
import random
import time
waittime = 5
flag = True
def main():
with ThreadPoolExecutor(max_workers=2) as executor:
executor.submit(timeWait)
executor.submit(keepScreen)
def timeWait():
global flag
time.sleep(waittime)
flag = False
def keepScreen():
screen_size = pyautogui.size()
global flag
while flag == True:
horizontal_move = random.randrange(screen_size[0])
vertical_move = random.randrange(screen_size[1])
duration = random.randrange(5)
pyautogui.moveTo(horizontal_move, vertical_move, duration=duration)
time.sleep(5)
if __name__ == '__main__':
main()
timeWait関数は先ほどまでのtimeWait関数とは異なり、とりあえず指定された秒数待つだけになっており、時間のカウントは表示されません。
keepScreen関数では最初に画面サイズを取得し、マウスを動かす先の位置と移動時間をランダムに取得し、マウスを動かし、最後に5秒待つという関数になっています。
これでtimeWait関数の中を行いたい処理に変更することで、その処理が終わるまでマウスが自動で動くことでPCがスリープしないと言うわけです。
他にも色々使い道がありそうな組み合わせなので、覚えておいて損はないと思います。
次回は小数から整数に変更する際の小数点以下の取り扱い(切り捨て、四捨五入、切り上げ、切り下げ)を見ていきましょう。
ではでは今回はこんな感じで。
コメント