9

この質問を書くことで解決したい問題がありますが、そうでない場合は投稿して、誰かが助けてくれるかどうかを確認します.

HTTP を介した COMET スタイルのロング ポーリングを利用するリアルタイム チャット サーバーとやり取りするために、クライアント ライブラリ (書き方が悪いと感じます) を使用しています。特定の状況でロング ポールをキャンセルする際に問題が発生し、同時実行処理コードを追加する必要があるのではないかと考えていますが、以下の理由により、これを行う最善の方法を見つけるのが難しいと感じています。

サブスクライブ コード(ロング ポールを開始する) は、次の大きなループとして実装されます。

doLongPoll()
{
    while(true)
    }
        //IF channel field boolean unsubscribe == TRUE, if so BREAK;
        //perform GET request (and store channel HTTPClient used for this call)
        //remove HTTPClient used for this call
        //IF channel field boolean unsubscribe == true, if so BREAK;
        //IF connection problem sleep(1500) then CONTINUE
        //post received data to listeners
    }
}

登録解除の呼び出し(別のスレッドで呼び出される)

unsubscribe()
{
    //set channel field boolean unsubscribe == FALSE
    //get channel HTTPClient and shutdown
}

操作がインターリーブしている問題のケースを特定しました。私には、これはコードがマルチスレッドであり、クライアント コードがスレッド セーフでないことが原因のようです。また、管理が不十分で再利用されてhttpClientいないことも役に立ちません。

私が抱えている問題の 1 つは、unsubscribe 呼び出しが次の getRequest の実行を停止しない場所の下にあります。

THREAD 1 (polling)                      THREAD 2
--------                                --------
do unsubscribe check (pass)
                                        unsubscribe called
                                        set unsubscribe = true
                                        check if httpClient saved (none)
perform getRequest (save HttpClient first)

この問題への最善のアプローチが何であると人々が考えているかを知りたいです(時間も限られているので、コードをあまり書き直すことはできません!)

これを修正するには、スレッド 1 の最初の登録解除チェックから、実際の get 要求が実行される直前に保存されるまでの同期ブロックを使用し、同じロックを使用して登録解除メソッドを同期できると考えました。httpClient言及された最初の同期ブロックが 1 つのメソッド呼び出しで開始され、(lib の記述方法により) メソッド呼び出しチェーンのさらに下で終了するため、これは現時点では実用的ではありません。

または、リクエストごとではなくチャネルごとに1httpClient作成するだけで、常にシャットダウンすることができ、同期を無視する可能性があります(私は思います)。

または、以下で提案されているように、同じ目的で割り込みを使用できます

どんな提案でも大歓迎です - 進展があれば編集します!

ありがとう

4

2 に答える 2

3

この問題の結果としてJavaConcurrencyin Practiceを購入しましたが、第7章:キャンセルとシャットダウンでこの問題と非常によく似た問題について議論していることがわかりました。これは引用で要約できます。

中断は通常、キャンセルを実装するための最も成功した方法です

HttpClientが割り込みをサポートしないソケットIOをブロックしているため、2つのアプローチがあります。を割り当て、実際の呼び出しhttpClientの直前に割り込みをチェックし、メソッドでスレッドを中断してから呼び出します。これは私の問題を解決しているようで、非常に単純な変更でした。インターリーブの問題はもうありません!httpClient.execute()unsubscribe()httpClient.getConnectionManager().shutdown();

また、ブールunsubscribeフィールドをvolatile、以前に実行すべきだった提案どおりに設定しました。ただし、これだけでは問題は解決しませんでした。

于 2013-01-11T10:23:42.937 に答える
2

1)購読解除を揮発性として設定します

2)確実性を高めるために、アクセス(読み取り/書き込み)を保護して、たとえばセマフォでサブスクライブを解除します

于 2013-01-08T13:40:20.640 に答える