この記事は 鈴鹿高専 Advent Calendar 2019 にも登録しました。
経緯
- #procon30の出場作品での出来事
- 組み込み系のコードをpython __2.7__で書いていた
- 自分がセンサ類のモジュール3つを担当した
- モジュール単体では、
Ctrl+C
で正常終了した(定期的なセンシングのため、デーモンプロセスと考えておk) - 全体制御を👨💻に任せていた
- 統合テストでは
Ctrl+C
でも終了しない - 👨💻は問題を放置し、__
Ctrl+Z
とkill -9
で強引に__プロセスを終了させていた - __本選後__に原因を発見し、今でも忘れられないので記事にする
問題のプログラム
👨💻は参照渡し時にイミュータブル(変更不可)な変数は変更されないことを知らなかったらしい。(ドキュメント)
main.py
import os
import threading
import time
import foo_1 # foo_1.py
std_lock = threading.Lock() # 標準入力時の排他制御
if __name__ == "__main__":
foo_1_thread = threading.Thread(target=foo1.foo_1,args=(std_lock,))
foo_1_thread.start()
while True:
try:
with std_lock:
print("thread name:{0}, ident:{1}".format("main", threading._get_ident()))
time.sleep(1)
except KeyboardInterrupt:
break
foo_1.py
import time
import threading
terminate = False
def foo_1(std_lock):
while False == terminate: # while == True じゃ止まんないから変えといたYo!^^ by👨💻
with std_lock:
print("thread name:{0}, ident:{1}, terminate:{2}".format("foo_1", threading._get_ident(),terminate))
time.sleep(1)
実行結果
$ python main.py
thread name:foo_1, ident:140618926196480, terminate:False
thread name:main, ident:140618956166976
thread name:main, ident:140618956166976
thread name:foo_1, ident:140618926196480, terminate:False
thread name:main, ident:140618956166976
thread name:foo_1, ident:140618926196480, terminate:False
^C
thread name:foo_1, ident:140618926196480, terminate:False
thread name:foo_1, ident:140618926196480, terminate:False
thread name:foo_1, ident:140618926196480, terminate:False
^Z
[1] + 15221 suspended python main.py
止め方
リスト型はミュータブル(変更可能)なので、terminate = False
をterminate = [False]
にして、terminate[0]
をいじれば止まる。
が、そんな止め方はイケてないので、threading.Thread
を継承し、スレッドをデーモンに設定して止まるようにした。
main.py変更箇所
- foo_1_thread = threading.Thread(target=foo1.foo_1,args=(std_lock,))
+ foo1_thread = foo_1.foo_1(std_lock)
foo_1.py
import time
import threading
class foo_1(threading.Thread):
def __init__(self,std_lock):
super(foo_1, self).__init__()
self.daemon = True
self.std_lock = std_lock
def run(self):
while True:
with self.std_lock:
print("thread name:{0}, ident:{1}".format("foo_1", threading._get_ident()))
time.sleep(1)
実行結果
$ python qiita_test.py
thread name:foo_1, ident:140555638449920
thread name:main , ident:140555668420416
thread name:foo_1, ident:140555638449920
thread name:main , ident:140555668420416
thread name:foo_1, ident:140555638449920
thread name:main , ident:140555668420416
^C
ドキュメントは英語でもちゃんと読みましょう