注意:この記事は情報に誤りがあるかもしれない
Python の ThreadPoolExecutor は基本的に with 内で動かすが、実際のスレッド数を超える値を max_workers に指定すると予想外のことが起きる。
例えば 3 つのプロセスを並列化したいとき、max_workers を 4 にすると、4 - 3 = 1 のゾンビ・プロセスが生まれる。全体のプログラムがデーモン化されている場合、この 1 個のスレッドが with で回収されない。
例えば gunicorn で動かす Django または Flask のプログラム内に ThreadPoolExecutor で並列化した関数があるとする。この ThreadPoolExecutor で max_workers を関数の数よりも多く設定していると
13474 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13510 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13511 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13512 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13513 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13514 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13549 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13550 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13551 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13552 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13553 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13578 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13579 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13580 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13581 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13582 gunicorn --bind 127.0.0.1:8080 main:app --daemon 13586 gunicorn --bind 127.0.0.1:8080 main:app --daemon
などとなる。これらのゾンビ・デーモンはシグナルで強制終了されない限り消えない。
デーモンになっていないプログラム、例えばローカルで動かしている Flask では max_workers の値にかかわらず、余ったスレッドは with で消される(たぶん)。
また ThreadPoolExecutor は OS に依存するため、実際は Pebble などを使う。Python の並列化でシグナルを直接扱っているソースコードをよく見かけるが、たぶん避けたほうがいい。