2009-07-13 10 views
4

.NETを使用するサーバの基本アルゴリズムはではありません。 'クライアントごとのスレッド'に基づいていますか?クライアントごとのスレッドではないコードを作成する

編集私は、サーバーが使用している一般的なプロセスを説明する基本的な3、4多分5行のアルゴリズム/ psuedocode /パターンを探しています。これとは逆

何か:


open a server socket // this uses the port the clients know about 

while(running)  
    client_socket = server_socket.listen  
    fork(new handler_object(client_socket)) 
+0

あなたは何を達成しようとしていますか? 「クライアントごとのスレッド」のどの側面を避けたいですか? –

答えて

1

スレッドプールは、サーバがクライアントよりも少ないスレッドを持つことができるようになります。

+0

だから、アルゴリズムはスレッドプールを使用するでしょうが、どのようなコンテキストでですか?使用される基本的なアルゴリズムは何ですか? psuedocodeの3,4,5行を探しているだけです。 –

2

イベントループ。ソケットが書き込み可能になるのを待ち、書き込みを待つ、接続を待つ、受け入れるなど。このアプローチは、通常はクライアントの状態を追跡するためにクロージャ以上のものを必要としないので、ほとんどの場合スレッドよりも拡張性があります。

2

広範で一般的なもの(つまり、ネットや他のプラットフォームに固有のものではない)では、サービスはプログラムされています2つの方法のいずれか、または両方の組み合わせで

接続ごとのスレッド:注意しておきますが、これはカーネルスレッドやプロセスなどの単一のスケジューリングエンティティ、あるいはユーザー空間でプラットフォームによって実装された軽いスレッドまたはコルーチンを使用します。単一の要求を処理することに順調に関わっています。

例の擬似コード:上記のサンプルの

function run(connection) 
    while(connection is open) 
     frobnicate(connection) 

function main 
    listener = new Listener(port) 
    threads = new Collection<Thread>() 
    while(running) 
     connection = listener.accept() 
     threads.add(new Thread(run, connection)) 
    join(threads) 
    exit() 

重要な機能:

  • これは、あなたがそれを置くとおりに、接続モデルごとのスレッドに従います。スレッドで の接続が終了すると、接続は終了します。
  • このプログラムを現実世界で実行する場合は、 が数百スレッド以上を処理する必要があります。ほとんどのプラットフォームは、このレベルの約 で解約を開始します。いくつかは、同時接続数十万台の の中で、さらに多くのスレッドを処理するように特別に設計されています。
  • 接続はマルチスレッドで処理されますが、実際には着信接続を受け入れる はメインスレッドでのみ処理されますが、悪意のあるホストが接続しようとしたが故意に失敗した場合はDoSのポイントになる可能性があります。
  • 実行を停止する時間があることをプログラムが検出すると、メインスレッドは終了時に終了しないように、他の スレッドに参加します。再び、つまり、プログラムは、すべての古いスレッドがデータの処理を終えるまで終了しないことを意味します。
    代わりに、結合を使用しないことがあります。この場合、他のスレッドは が無計画に終了します。

ノンブロッキング:1つの実行スレッドが複数の接続を管理し、特定の接続がストールしたとき、データがネットワークを介して移動するのを待っているとき、またはディスクから読み込むときにプロセスがスキップするそれ以上の接続で動作します。

例の擬似コード:このスニペットの

function main 
    connections = new Collection<Connection>() 
    listener = new Listener() 
    connections.append(listener) 
    foreach connection in connections: 
     if connection.ready(): 
      if connection is listener: 
       connections.add(connection.accept()) 
      else: 
       if connection is open: 
        nb_frobnicate(connection) 
       else: 
        connections.remove(connection) 
     yield() 
     if(not running) 
      exit() 

特徴:マルチスレッド・バージョンで

  • は、各スレッドが単一の接続を処理しました。 その接続が使用する準備ができていない場合、スレッドはブロックし、別のスレッドは の作業を実行できます。この実装でfrobnicateがブロックされていると、災害になります。たとえ1つの接続が準備できなくなっても、他の接続は作業を行うことができます。 これに対処するには、接続で非ブロック操作を使用するように注意している代替機能が使用されます。これらの読み取りと書き込みはすぐに戻ります。 呼び出し側にどれくらいの作業を実行できるかを示す値が返されます。その場合、 がまったく動作しない場合、プログラムは接続がより多くの作業の準備ができていることを知っているので、ただちに としてもう一度試す必要があります。私は nb_frobnicateに名前を変更して、この機能がノンブロッキングであることを注意していることを示しています。
  • ほとんどのプラットフォームは、接続上でループを抽象化できる機能、すなわち を提供し、それぞれにデータがあるかどうかを確認します。これは、select()または poll()のいずれかと呼ばれます(どちらも利用可能です)。それでも、私は 以上のネットワーク接続で作業したいかもしれないので、この方法で表示することにしました。私が知る限り、 は、すべての可能なブロック操作を総称的に待つことはできません。 ディスクIO(Windowsのみ)、タイマー、ハードウェア割り込み(画面の更新など)を待つ必要がある場合は する必要がありますさまざまなイベントソースを手動でポーリングします。
  • は、yield()関数です。この関数は、IO割り込み が発生するまでスリープします。これは、IOが処理される準備ができていない場合に必要です。これを行わなかった場合、 プログラムはビジー待機し、各ポートをチェックして再確認し、 にはデータが準備されていないことがわかり、不必要にCPUを浪費します。プログラムが ネットワークIO(またはPOSIXシステム上のディスク)で待機する必要がある場合は、 ブロッキングモードでselect/pollを呼び出すことができ、ネットワーク接続が可能になるまでスリープします。しかし、もっと多くの種類のイベントを待つ場合は のノンブロッキングバージョンを使用し、他のイベントソースをそのイディオムに従ってポーリングしてから、 がすべてブロックするようにしなければなりません。
  • 上記の例のスレッド結合には、ここでは対応しません。代わりに、非結合動作に似た です。 ポーリングコレクションからリスナーを削除してから同様の動作をしてから、一度exitを呼び出してください。 コレクションが空です。
  • プログラム全体は、単純なループと関数呼び出しで構成されています。 は、コンテキストスイッチのコストを招くことはありません。これの欠点は、 プログラムが厳密に順次であり、 が存在する場合、複数のコアを利用できないことです。ほとんどのサービスはIO になり、接続が準備される速度によって制限され、 架空のfrobnicate()機能のコストではないため、これはほとんど問題ではありません。

前述したように、これらを組み合わせて使用​​することで、マルチコアシステムでのCPU使用率の向上などの複数のスケジューリングエンティティの利点の多く、コンテクストの切り替えが少なくなるため負荷が軽減されます。この

例:このプログラムに関する

function worker(queue) 
    connections = Collection<Connection>() 
    while(running) 
     while(queue not empty) 
      connections.add(queue.pop) 
     foreach connection in select(connections) 
      if connection is open: 
       nb_frobnicate(connection) 
      else: 
       connections.remove(connection) 

function main 
    pool = new Collection<Thread, Queue<Connection> >() 
    for index in num_cpus: 
     pool[index] = new Thread(worker), new Queue<Connection> 
    listener = new Listener(port) 
    while(running)   
     connection = listener.accept() 
     selected_thread = 0 
     for index in pool.length: 
      if pool[index].load() < pool[selected_thread].load() 
       selected_thread = index 
     pool[selected_thread].push(connection) 
     pool[selected_thread].wake() 

ノート:

  • これは、シングルスレッドバージョンのインスタンスの束を作成しますが、彼らは マルチスレッドなので、彼らはと通信する必要がありますどういうわけか、お互いに。これは各スレッドに対して Queue<Connection>を使用して行われます。
  • また、シングルスレッドプログラムと同じ理由で ハンドラを使用していることに注意してください
  • ワーカースレッドのプールは、所有しているプロセッサの数によって制限されています。
    おそらく を一度に実行するよりも多くのスレッドを待機させることの利点がほとんどないためです。実際には、使用する最適なスレッド数は アプリケーションごとに異なる場合がありますが、CPUの数は十分な見込みが多いことがよくあります。
  • 以前のように、メインスレッドは接続を受け入れ、それらをワーカースレッドに渡します。 スレッドが既に他のスレッドで作業を完了し、準備が整うのを待っている場合は、 新しい接続がすぐに準備されていても、スレッドはそこに座ります。これを緩和するために、 メインスレッドはワーキングによってワーカースレッドに通知し、ブロッキング 操作を戻します。これが通常の読み取りだった場合は、おそらく というコードが返されますが、select()を使用しているため、準備完了の空のリストが返され、スレッドはもう一度そのキューを空にできます。

編集:追加のサンプルコード:

+0

これは意味があります。したがって、非ブロッキング接続は、オブジェクトを管理するスレッドのほうに向いています。ノンブロッキング方式の一般的なアルゴリズムはありますか? –

2

処理が必要であるものは何でもやって、準備ができていたソケット(クライアント)を決定するために、Selectを呼び出し、その後、読み出し及び/又はそれらのソケットに書き込むループときにないソケット準備が整いました。

擬似コード:

loop { 

    to_read.add(client1); 
    to_read.add(client2); 

    select(to_read, timeout = 0.5); 

    for client in to_read { // to_read is modified by select 
     data = client.read 
     handle(data) 
    } 

    if to_read is empty { 
    do_bookeeping() 
    } 
} 
+0

有望そうです。選択を使用するには、一般的なアルゴリズム/擬似コード/パターンがありますか? –

+0

素晴らしい!ありがとうございました! –

0

あなたは、スレッドプールによって制限され、要求ごとにスレッドを行うことができます。しかし、これを処理するための非同期メソッドに興味があるかもしれません。このページでは、アプローチがどのように動作するか非同期の良い仕事をしていません:

1

例えば、イベント・キューとスレッドプールを使用します:また

when [something happens] then 
    [create an event for something] 
    [put it in the queue] 

: - >コールバックAuthenticatedStreamで

while [something in queue] 
    if [thread is available] then 
     remove [thing] from queue 
     run [thing-response] on [available thread] 
    else [wait a little while] 

このアーキテクチャは

+0

興味深い。どのようにソケットにそれを適用するのだろうか? –

+0

@ [不明]: "接続要求" = [何か起こる]、queue.Add(新しいMyEvent(MyEnum.ConnectionRequested、connectionInfo))=イベントを作成し、キューに入れます。イベントタイプへの応答は、(connectionInfoを介して)資格をチェックしたり、接続を確立したりすることです。新しい接続は追加イベントのソースになる可能性があります[別の回答のソケットリストポーリングを参照] –

+0

ありがとうSteven。 –

0

Socket.BeginAccept非常にスケーラブルです。 BeginAuthenticateAsServer - >コールバックストリーム。 BeginRead - >コールバックで新しいBeginRead、処理要求、ストリームをポストします。回答はBeginWriteです。

あなたが本当にそれを必要としない場合は、認証されたストリームの一部(SSLまたはKerberos/NTLM)を取り出すことができ、それは次のようになります。 Socket.BeginAccept - >コールバックソケットインチBeginReceive - >コールバックで新しいBeginReceiveをポストし、リクエストを処理してからソケットを追加します。回答はBeginWriteです。

Also see Asynchronous Server Socket Example

+0

を要約してくださいアルゴリズムが使用されていますか? –

+0

典型的なイベント駆動型アプリケーション(イベントはソケット受け入れ、読み取り完了、書き込み完了)。スタックベースのクライアント状態(スレッド)の代わりに、ヒープベースのクライアント状態(つまり、接続/状態を表すオブジェクト)があります。 –

2

イアン・グリフィスは、クライアントごとにスレッドを使用することなく、複数のクライアントを処理するexcellent (.NET) introductionを持っています。

+0

それは素晴らしい記事です。どうもありがとうございます! –

関連する問題