web-dev-qa-db-ja.com

「数十万」のスレッドが必要になるのはいつですか。

Erlang、Go、およびRustすべての方法で、安価な「スレッド」/コルーチンによる並行プログラミングをサポートしていると主張しています。 Go FAQ は次のように述べています。

同じアドレス空間に何十万ものゴルーチンを作成するのが現実的です。

Rustチュートリアル はこう言っています:

タスクの作成は従来のスレッドよりもはるかに安価であるため、Rustは、典型的な32ビットシステムで数十万の同時タスクを作成できます。

Erlangのドキュメント は言う:

数十万または数百万ものプロセスを持つErlangシステムをサポートするために、233ワードのデフォルトの初期ヒープサイズはかなり控えめです。

私の質問:どのようなアプリケーションで、非常に多くの同時実行スレッドが必要ですか?最もビジーなWebサーバーだけが数千の同時ビジターを受け取ります。私が作成したBoss-worker/job-dispatchingタイプのアプリケーションでは、スレッド/プロセスの数が物理コアの数よりもはるかに多い場合、ヒットが減少します。数値的なアプリケーションには理にかなっていると思いますが、実際には、ほとんどの人が並列処理を、これらの新しい世代の言語ではなく、Fortran/C/C++で書かれたサードパーティのライブラリに委譲しています。

31
user39019

1つの使用例-WebSockets:
websocketは単純なリクエストと比較して存続期間が長いため、ビジー状態のサーバーでは、時間の経過とともに多くのwebsocketが蓄積されます。マイクロスレッドは、優れた概念モデリングと比較的簡単な実装を提供します。

より一般的には、多数の自律ユニットが特定のイベントの発生を待機しているケースは、良いユースケースであるはずです。

19
kr1

Erlangが本来何をするために設計されたのか、それは電気通信を管理することであったと考えるのに役立つかもしれません。ルーティング、切り替え、センサーの収集/集約などのアクティビティ.

これをウェブの世界に持ち込む-Twitterのようなシステムを考えてみましょう。システムはおそらくWebページの生成にマイクロスレッドを使用しませんが、ツイートの収集/キャッシング/配信でそれらを使用できます。

この記事 はさらに役立つかもしれません。

15
Dave Clausen

変数の変更が許可されていない言語では、状態を維持するという単純な行為には、別個の実行コンテキストが必要です(ほとんどの人はスレッドを呼び出し、Erlangはプロセスを呼び出します)。基本的に、すべてが労働者です。

カウンターを維持する次のErlang関数を考えてみます。

counter(Value) ->
    receive                               % Sit idle until a message is received
        increment -> counter(Value + 1);  % Restart with incremented value
        decrement -> counter(Value - 1);  % Restart with decremented value
        speak     ->
            io:fwrite("~B~n", [Value]),
            counter(Value);               % Restart with unaltered value
        _         -> counter(Value)       % Anything else?  Do nothing.
    end.

従来のOO C++やJavaのような言語では、クラスをプライベートクラスメンバー、状態を取得または変更するパブリックメソッド、および各カウンターのインスタンス化されたオブジェクトで構成することにより、これを実現します。 Erlangは、インスタンス化されたオブジェクトの概念をプロセスに置き換え、メソッドの概念をメッセージに置き換え、状態を tail Calls で維持して、関数を新しい状態を構成する任意の値で再起動します。モデル-そしてErlangのraison d'êtreのほとんどは、言語がメッセージキューを使用してカウンター値へのアクセスを自動的にシリアル化することです。並行コードを非常に簡単に実装でき、高い安全性を備えています。

おそらく、コンテキストスイッチは高価であるという考えに慣れているでしょう。これは、ホストOSの観点からは依然として当てはまります。 Erlangランタイムはそれ自体がチューニングされた小さなオペレーティングシステムであるため、OSが実行するコンテキスト切り替えの数を最小限に抑えながら、独自のプロセス間の切り替えが迅速かつ効率的です。このため、何千ものプロセスを持つことは問題ではなく、推奨されます。

11
Blrfl

私の質問:どのようなアプリケーションで、非常に多くの同時実行スレッドが必要ですか?

1)言語が「スケーリング」するという事実は、物事がより複雑になると、その言語を捨てる必要性が少なくなることを意味します。 (これは「製品全体」の概念と呼ばれます。)多くの人々はこのまさに理由でNginxのためにApacheを捨てます。スレッドのオーバーヘッドによって課せられる「ハード制限」に近い場所にいると、怖がってそれを乗り越える方法について考え始めるでしょう。 Webサイトが取得するトラフィック量を予測することはできないため、スケーラブルなものにするために少しの時間を費やすのは妥当です。

2)リクエストごとに1つのゴルーチンが開始するだけです。内部でゴルーチンを使用する理由はたくさんあります。

  • 100の同時リクエストを持つWebアプリを考えますが、各リクエストは100のバックエンドリクエストを生成します。明白な例は、検索エンジンのアグリゲーターです。しかし、ほとんどすべてのアプリで、画面上の「領域」ごとにゴルーチンを作成し、順次ではなく個別に生成することができます。たとえば、Amazon.comのすべてのページは150以上のバックエンドリクエストで構成されており、お客様のために組み立てられています。それらは並列であり、順次ではなく、各「領域」は独自のWebサービスであるため、気付かないでしょう。
  • 信頼性とレイテンシが最も重要なアプリを検討してください。おそらく、各着信要求がいくつかのバックエンド要求を起動し、 最初に戻ったデータを返す にする必要があります。
  • アプリで行われた「クライアント参加」を検討してください。 「要素ごとにデータを取得」と言う代わりに、一連のゴルーチンをスピンオフできます。クエリするスレーブDBがたくさんある場合は、魔法のようにN時間速くなります。そうしないと、遅くなることはありません。

スレッド/プロセスの数が物理コアの数よりもはるかに多い場合、ヒットが減少します

プログラムを [〜#〜] csp [〜#〜] に分割する理由はパフォーマンスだけではありません。実際にプログラムを理解しやすくし、いくつかの問題ははるかに少ないコードで解決できます。

上記のスライドのように、コードに同時実行性を持たせることは、問題を整理する方法の1つです。ゴルーチンがないのは、あなたの言語にMap/Dictonary/Hashデータ構造がないのと同じです。それがなくても問題ありません。しかし、それを手に入れれば、どこからでも使い始めることができ、プログラムを本当に単純化します。

以前は、これは「独自のマルチスレッドプログラミング」を意味していました。しかし、これは複雑で危険でした-レースを作成していないことを確認するためのツールはまだたくさんありません。そして、どうすれば将来のメンテナがミスをするのを防ぐことができますか?大きく複雑なプログラムを見ると、その方向に resources のLOTを費やしていることがわかります。

並行性はほとんどの言語の最初の部分ではないので、今日のプログラマーはなぜそれが彼らにとって有用であるのかについて盲点があります。これは、すべての電話と腕時計が1000コアに向かうにつれて、より明白になります。組み込みのレース検出ツールで出荷します。

4

Erlangの場合、接続または他のタスクごとに1つのプロセスを持つことが一般的です。したがって、たとえば、ストリーミングオーディオサーバーは、接続されたユーザーごとに1つのプロセスを持つ場合があります。

Erlang VMは、コンテキストスイッチを非常に安価にすることにより、数千または数十万のプロセスを処理するように最適化されています。

2
Zachary K

通信用に設計されたErlangの簡単な例:ネットワークパケットの転送。 1つのhttp要求を実行すると、数千のTCP/IPパケットが存在する可能性があります。これに加えて、全員が同時に接続し、あなたのユースケースがあります。

大企業が社内で使用し、注文や必要なものを処理する多くのアプリケーションを検討してください。スレッドが必要なのはWebサーバーだけではありません。

1

便利。マルチスレッドプログラミングを始めた頃は、楽しみのために多くのシミュレーションとゲーム開発を行っていました。単一のオブジェクトごとにスレッドをスピンオフし、それをループごとに処理するのではなく、それ自体に任せることが非常に便利であることがわかりました。非決定的な動作によってコードが妨害されず、衝突がない場合は、コーディングが容易になります。現在、私たちが利用できるパワーがあるので、それに戻ると、その多くの個別のオブジェクトを処理するのに十分な処理能力とメモリがあるため、数千のスレッドがスピンオフすることは容易に想像できます。

1
Brian Knoblauch