Linux におけるスレッド数の上限
並行 (Concurrent) 処理を実装する方法としてスレッドは非常に強力なツールです。 スレッドを使えば同時に1つの処理しか行えない既存のプログラムに大きな修正を加えることなく、 並行処理を実装することが可能です。 またイベントとコールバックを複雑に組み合わせた非同期的なプログラムに比べて、 同期的なプログラム (例えばファイルの読み込みにコールバックが出てきたりしない普通のプログラム)は プログラムの流れを自然に書くことができ、 可読性・保守性・テスト、デバッグのしやすさの面で遥かに優れています。 スレッドを使うとプログラムをそれほど複雑・難読化にせずに並行処理が実装できます。
一方でスレッドを使った並行処理には欠点もあります。 欠点の1つは、スレッドモデルでは1つの処理に対して1つのスレッドを用意するので、 システムのスレッド数の上限で同時に行える処理の数が決まってしまう点です。 サーバーアプリケーションの場合、1つのリクエストやコネクションに対して1つのスレッドを用意することになるので、 同時に捌けるリクエスト数がシステムのスレッド数で決まってしまいます。
そしてスレッド数の上限はハードウェアのパフォーマンス上の限界よりも低く設定されてしまっている場合があります。 そのため、スレッドモデルで書かれたプログラムで多数のリクエストを捌くためには、 スレッド数の上限を決めてしまう制約にどのようなものがあり、それらの制約をどのように緩めればいいかを把握しておく必要があります。
テスト用のコードを github にアップロードしてあります。
ulimit によるプロセス数制限
ユーザ毎に起動できるプロセス数の制限が存在します。
スレッドもプロセスの一種なのでこの上限を超えてスレッドを作成することはできません。
この上限は ulimit -u
で確認できます。
$ ulimit -u
unlimited
この上限は /etc/security/limits.conf
をで設定できます。例えば
yunabe soft nproc 10000
yunabe hard nproc unlimited
みたいなエントリを足せばよいです。編集後は再ログインか ulimit -a
の実行が必要。
システム全体でのスレッド数の上限
Linux にはシステム全体でのスレッド数の上限が設定されています。この値は
cat /proc/sys/kernel/threads-max
で確認できます。 この上限値を挙げるには /etc/sysctl.conf
に
kernel.threads-max = 100000
のようなエントリを追加して、sudo sysctl -p
を実行してシステムに反映します。
/proc/sys/vm/max_map_count
- pthread は 1スレッドあたり、2つのメモリマップを必要とするようです。
- 1つのプロセスで使えるメモリマップの数には上限があります。
- この上限値は
cat /proc/sys/vm/max_map_count
で確認可能。これを半分にした数を超えてスレッドを作ることはできません。 - この値も
/etc/sysctl.conf
にvm.max_map_count = 200000
みたいなエントリを足すことで増やすことができる。sudo sysctl -p
で反映。 - 参考:https://listman.redhat.com/archives/phil-list/2003-August/msg00005.html
プロセス番号の最大値
- プロセス番号の最大値
cat /proc/sys/kernel/pid_max
- デフォルト値は 32768 (1 << 15)。32 bit 環境では上げられない。 64 bit 環境では 2^22 (4Mぐらい) まで上げることが可能。
man 5 proc
スタックと仮想メモリサイズ制限 (32bit環境限定)
pthread のスレッドごとのスタックサイズがデフォルトで 2MB ぐらいらしい (要出典) ので、 1つのプロセスでたくさんのスレッドを作ると 32bit 環境での仮想メモリのサイズ 4.2GB (スタックに使えるのはこれより当然小さい) を各スレッドのスタックだけで超えてしまいます。 4.2GB/2MB = 2000 なのでこの上限はかなり小さい。
解決策はスレッドごとのスタックサイズを小さくするしかありません。 64bit 環境では仮想メモリが42億倍あるのでこの問題は起こりません。