🐛

言語のスレッド実装の雑な話(Green threadからGoのgoroutineまで)

2023/01/09に公開

Twitterで "green thread" という単語をたまたま見かけたので、知っていることをつぶやいたよ。

Green thread

https://twitter.com/tetsu_koba/status/1612315228775284736?conversation=none

https://twitter.com/tetsu_koba/status/1612316733242773504?conversation=none

https://twitter.com/tetsu_koba/status/1612319829666717696?conversation=none

https://twitter.com/tetsu_koba/status/1612320543348502528?conversation=none

言語のスレッドとOSのスレッドの関係

N:1 mapping

言語のスレッドの全てがひとつのOSのスレッドの上で実行されるもの。その代表が上記のJavaのgreen thread。
OSのシステムコールを呼ぶときには必ずnonblockingモードを使い、EAGAIN または EWOULDBLOCKが返ってきたときには他のスレッドの実行権に譲るようにする必要がある。うっかりシステムコールでブロックされてしまうと、全部のスレッドが巻き添えになって動けなくなる。
スレッドの生成やコンテキストの切り替えは軽い。しかし、マルチコアを生かすことができないため、シングルコアの環境でのみ使用される。

1:1 mapping

OSのスレッドと言語のスレッドが1対1対応するもの。Javaでのnative thread実装などpthreadライブラリをラップしてスレッドを実装されることが多い。
マルチコアへのCPUリソースの配分はOSがやってくれる。
欠点はスレッドの生成のたびにOSのcloneシステムコールを呼ぶので重い。大量に(何千万とか)スレッドを生成するとそれぞれのスタックエリアがメモリを圧迫する。

M:N mapping

N:1 mapping でOSのスレッドをひとつでなく複数に分散させるもの。
スレッドの生成は軽く、マルチコアに対応でき、ブロックされるシステムコールも自由に呼ぶことができる。
理想ではあるが、実装が複雑になりすぎるので現実的ではないと昔は言われていた。

現在これを実現しているのがGo言語のgoroutine。
Goのcgoで特にシステムコール呼び出しに制限がない[1]のはGoのランタイムの中でスレッドの状態を監視していて、システムコールによるブロックが発生すると、新たなOSスレッドを生成して残りのgoroutineをそっちに移して実行するようになっているため。

参考

https://en.wikipedia.org/wiki/Thread_(computing)

脚注
  1. 唯一の例外はforkを単体で呼べないこと。forkAndExecならできる ↩︎

Discussion