2017-01-03 6 views
0

私は次のようにBerkeleyソケットのselect関数を利用しています。バークレーソケットでのスレッド通知の使用

/*Windows and linux typedefs/aliases/includes are made here with wsa 
junk already taken care of.*/ 

/**Check if a socket can receive data without waiting. 
\param socket The os level socket to check. 
\param to The timeout value. A nullptr value will block forever, and zero 
for each member of the value will cause it to return immediately. 
\return True if recv can be called on the socket without blocking.*/ 
bool CanReceive(OSSocket& socket, 
    const timeval * to) 
{ 
    fd_set set = {}; 
    FD_SET(socket, &set); 
    timeval* toCopy = nullptr; 
    if (to) 
    { 
     toCopy = new timeval; 
     *toCopy = *to; 
    } 

    int error = select((int)socket, &set, 0, 0, toCopy); 
    delete toCopy; 
    if (error == -1) 
     throw Err(); //will auto set from errno. 
    else if (error == 0) 
     return false; 
    else 
     return true; 
} 

私は(aotherクラスに包ま)ソケットのコンテナを見て、ソケットにアクセスする準備ができているかについての情報を保存する別の容器にIDを追加するクラスを書かれています。マップはunordered_mapです。

while(m_running) 
{ 
    for(auto& e : m_idMap) 
    { 
      auto id = e.first; 
      auto socket = e.second; 
      timeval timeout = ZeroTime; /*0sec, 0micro*/ 
      if(CanReceive(socket,&timeout) && 
       std::count(m_readyList.begin(),m_readyList.end(),socket) == 0) 
      { 
       /*only add sockets that are not on the list already.*/ 
       m_readyList.push_back(id); 
      } 
    } 
} 

私は多くの人が気づいていると確信しているように、このコードはめちゃくちゃ速く走ると明日(マップ内の1つのソケットだけで40%のCPU使用率)がないようにCPUをアップゴブルズ。私の最初の解決策は、毎秒の繰り返しを設定値に保つスマートな待機関数を用意することでした。それはいくつかの人々とうまくいくようでした。 私の質問はこれです:このメソッドを使わずにソケットが準備できたら、どうすれば通知することができますか?たとえそれが移植性を保つためにマクロジャンクを必要としても大丈夫です。私は、オペレーティングシステムが私のためにそれを見て、ソケットが準備が整ったときにある種の通知やイベントを受け取れるようにする方法があるかもしれないと思うだけです。ちょうど明確にするために、私はドットネットを使用しないことを選択しました。

ループは独自のスレッドで実行され、ソケットの準備ができたらソフトウェアの他の部分に通知を送信します。全体がマルチスレッドであり、この部分を除くすべての部分でイベントベースの通知システムが使用され、ビジー状態の待機中の問題が解消されます。私は事がOSに依存し、この分野では限られていることを理解しています。

編集:ソケットはBLOCKINGモードで実行されます(ただし、選択にはタイムアウトがないため、ブロックされません)が、専用のスレッドで操作されます。 編集:システムはスマートスリープ機能を搭載していますが、いくつかの通知システム(OSからの可能性があります)と比べて優れた性能を発揮します。

+1

なぜ 'recv'を呼び出さないのですか? 'recv'が何をするのかを予測しようとする' select'の余分な呼び出しの目的は何ですか? 'recv'を呼び出すだけで、何が起こるのか見てみませんか? –

+0

また、これを行うにはライブラリを使用するだけです。それを100%正しくすることは仕事の大事なことです。あなたが発見したように、最小限のコードでそれを偽造するのは苦しいです。 ASIOには魅力的なものがあります。 –

+0

David Schwartz:ソケットがすぐに私に何かを与えることがない場合、私は別のソケットに移動し、私に何かがあるかどうかを確認します。何千ものソケットがあります。 recvを呼び出すだけで1つのソケットをブロックすると、今すぐ準備ができている可能性のある他のソケットは無視されます。 no-blockオプションを指定してrecvを呼び出すと、まだ待っているので何も解決しません。 – Matt

答えて

1

まず、ソケットをブロックしたくない場合は、ソケットを非ブロックに設定する必要があります。 select関数は、後続の操作がブロックされないことを保証しません。これは、過去と現在を示すステータスレポート機能です。

第2に、これを実行する最善の方法はプラットフォームによって異なります。プラットフォーム固有のコードをたくさん書こうとしない場合は、Boost ASIOやlibeventのようなライブラリを使うべきです。

第3に、すべてのソケットで同時にselectをタイムアウトで呼び出すことができます。この関数は、ソケットのいずれかが読み込み可能(または読み込み可能)であればすぐに戻り、そうでない場合はタイムアウトまで待機します。 selectが返ってきたら、タイムアウトしたかどうかを報告し、そうでない場合は読み込み可能なソケットを報告します。

これは、単一のソケットが読めるとすぐに、すべてのプロセスから即座にプロセスを削除するために待機リストが多数存在するため、パフォーマンスが非常に悪くなります。しかし、それは妥当な移植性で行うことができます。

+0

select()およびpselect()は、プログラムが複数のファイル 記述子を監視し、ファイル記述子の1つまたは複数が、ある種の入出力操作(例えば、入力可能)に対して "ready"になるまで待つ。 ファイル・ディスクリプタは、対応するI/O操作(例えば、ブロックなしのread(2)または十分に小さいwrite(2)の )を実行することができる場合には、準備完了とみなされる。投稿者:http://man7.org/linux/man-pages/man2/select.2.html – Matt

+0

複数のソケットを一度に呼び出すと、1つ以上のソケットが準備完了であるとしか言わず、個別にチェックする必要があります誰が準備ができているのですか?いくつかは準備ができていないかもしれません。 – Matt

+2

'select'関数は、渡すファイルディスクリプタセットを変更して、どのファイルディスクリプタセットが用意されているかを示します。 'select'がヒットを返すとき、' select'を呼んだときと戻ったときの間に、ある点で*ソケットが準備されていたことを意味します。それは、将来の準備と、それが焼かれたと仮定した人々を保証するものではありません。 –

0

この メソッドを使用せずにソケットが準備完了状態になったら通知を受けるにはどうすればよいですか?

これがselect()の目的です。アイデアは、select()への呼び出しが、あなたが渡したソケットの少なくとも1つ(FD_SET()経由)が読み込み可能になるまでブロックするべきだということです。 select()が返った後、どのソケットがread-for-read(FD_ISSET()を呼び出して)であるかを知ることができ、それらのソケットでrecv()を呼び出してデータを取得して処理することができます。その後、再びループし、select()の中で再びスリープ状態に戻り、無限に繰り返します。この方法では、最小限のCPUサイクルを使用しながら、できるだけ早くすべてのタスクを処理します。

全体の事は、マルチスレッドと(この 一部を除く)、それのすべての部分忙しい 待っている問題を解消するイベントベースの通知システムを使用していますです。

スレッドがselect()内でブロックされていて、すぐに何かを実行したい(タイムアウトに頼らずに、遅く非効率的である)場合は、そのスレッド内のselect()をすぐに返すような方法が必要です。私の経験では、これを行う最も信頼できる方法は、pipe()またはsocketpair()を作成し、ファイル記述子のペアの一端をread-for-read fd_setに含めることです。次に、別のスレッドがそのスレッドを起床させたいときは、そのペアのもう一方の端にバイトを送信するだけでそれを行うことができます。これにより、select()が戻るようになり、スレッドは1バイトを読み込み(そしてそれを投げ捨てる)、起きた後に何をしていようとも何でもします。

+0

タイムアウトが0に設定されているため、私のケースでは選択がブロックされません。ソケットが準備ができているかどうかはすぐにわかります。準備が整っていれば、それは待ち行列に行きます。そうでなければ何も起こりません。パイプやソケットのペアについてのあなたのアイデアはいいアイデアです。実際には、タイムアウトが終了する前に選択するコールを中断することで利益を得ることができる他のコードがあります。ありがとうございました。 – Matt

+2

タイムアウト引数をNULLのままにしておくと、無期限にブロックされます(または、渡されたソケットの1つがxの準備ができているまで)。 CPUを回転させたくない場合は、少なくとも1つのソケットが準備完了するまでブロックする必要があります。 –

+0

そのコメントは私が一緒に行く答えです。私はブロックできません(他のソケットはブロック中に準備ができているかもしれません)ので、適応スリープ機能を使って1秒あたりのループを妥当なレベルに保ちます(ループは10マイクロ秒ごとに1回、 CPU使用率を1%以下に保ちます)。私は毎秒1000万回のチェックが十分に効率的でなければならないと思います。ご相談に応じていただきありがとうございます。 – Matt

関連する問題