回答編集履歴

1 サンプルコードの追加

uni8inu

uni8inu score 127

2017/01/05 10:46  投稿

こんにちは。自分もちょうど並行処理調べてたので、何かしらヒントになれば。
基本的事項として、**オライリーの入門Python3本**によるとThreadは強制停止しにくいらしいです。(p335)
また、[Python公式Document](http://docs.python.jp/3/library/threading.html#threading.TIMEOUT_MAX)に記載してある、以下の事を念頭に置いておくと良い感じがします。
> 現状では、優先度 (priority) やスレッドグループがなく、スレッドの破壊 (destroy)、中断 (stop)、一時停止 (suspend)、復帰 (resume)、割り込み (interrupt) は行えません。
##単体で実行した場合の挙動
> 単体で実行したら想定通り子スレッドが停止したら孫スレッドも停止します。
とのことですが、以下のコードにすると、引き続き孫Threadが動作していました。stop()が機能していないように思えます。
```python
# sample_thread.py
(前略)
if __name__ == '__main__':
   th2 = ThreadTwo(name='ThreadTwo')
   th2.start()
   time.sleep(10)
   th2.stop()
   time.sleep(100) # 追記 / stopで孫threadが止まるはずなのに、実行続く
コード  
```
元々のコードでは、main終了時に、デーモンスレッドだけになる=孫threadが停止し、プログラム全体が終了というフローになっていたと考えます。[公式Document](http://docs.python.jp/3/library/threading.html#thread-objects)
> スレッドには “デーモンスレッド (daemon thread)” であるというフラグを立てられます。 このフラグには、残っているスレッドがデーモンスレッドだけになった時に Python プログラム全体を終了させるという意味があります。フラグの初期値はスレッドを生成したスレッドから継承します。フラグの値は daemon プロパティまたは daemon コンストラクタ引数を通して設定できます。
Django上で実行したものはstop()が機能していないのでthreadが止まらず、また、django本体のスレッドは停止していない(?)ので、特にデーモンもkillされず、ThreadTwo終了/ BotClass終了まで処理が進んでしまっていたと考えます。
対策としては、threadではなくmultiprocessingを使うと良いかもしれません。terminate()関数により強制停止が可能なようです。(あまり知識がないので、もっと良い方法があるかもしれませんが…)
対策としては、threadではなくmultiprocessingを使うと良いかもしれません。terminate()関数により強制停止が可能なようです。(あまり知識がないので、もっと良い方法があるかもしれませんが…)
##(追記)stop()の実装
やりたいこととぜんぜん違うかもしれませんが、以下でstop的な動作になりました。
threadをkillする手段が無い以上、worker(thread)内部に停止コード仕込むしかない、と考えます。
[Qiitaでもpythonのthread記事は荒れてました。](http://qiita.com/tag1216/items/2dcb112f8018eb19a999#_reference-2affe5b053d4748ec49b)
```python
class BotClass:
# (前略)
   def _thread_three(self):
       for i in range(5):
           if not self.listening: # add
               break # add
           time.sleep(2)
           print(threading.currentThread().getName() + ': ' + str(i))
   def _thread_four(self):
       for i in range(5):
           if not self.listening: # add
               break # add
           time.sleep(3)
           print(threading.currentThread().getName() + ': ' + str(i))
```

思考するエンジニアのためのQ&Aサイト「teratail」について詳しく知る