3

エンコーダーのエンコード メソッドは同時に実行されますか? エンコード方法が異なるスレッドによって並行している可能性があることに気付きました。パイプラインは次のように定義します。

Channels.pipeline(
    idleHandler,
    new AmfDecoder<GameEvent>(GameEvent.class),
    new AmfEncoder<GameEvent>(),
    concurrencyHandler,
    new WebHandler());

エンコーダー:

public class AmfEncoder<T extends IAmfEvent> extends OneToOneEncoder{
private final SerializationContext serializationContext = new SerializationContext();
private final Amf3Output amfout = new Amf3Output(serializationContext);

@Override
protected Object encode(ChannelHandlerContext arg0, Channel arg1,
        Object arg2) throws Exception {
    T e = (T)arg2;
    ByteArrayOutputStream byteoutStreamSize = new ByteArrayOutputStream();
    amfout.setOutputStream(byteoutStreamSize);
    amfout.writeObject(e.getBody());
    // byteoutStreamSize has small probability become empty at here, in debug mode I can sure e.getBody() has data
    // I thought byteoutStreamSize might be empty by another thread call "amfout.flush()" or "amfout.reset()"
    amfout.flush();
    //...
    amfout.reset();
}

}

Channel.write の呼び出しは、スレッドが netty のワーカー スレッドまたは Exeutionhandler 内のスレッドに属するだけではありません。自分で作成したスレッド プールがあり、Channel.write() を呼び出します。amfout と serializationContext の 2 つの変数を encode() 関数に移動してローカル変数にすると、問題はなくなります。

Doc によると、ChannelPipeline はスレッド セーフです。netty 3.4.5 で「追加」、「削除」が見つかりました...操作はロックされていますが、sendDownstream と sendUpstream にはロックがありません。したがって、ワーカー スレッド プールまたは ExecutionHandler スレッド プールに属さないスレッドがあり、これらすべてのスレッドが Channel.write() を呼び出すと、デコーダーとエンコーダーで同時発生の問題が発生します。

4

2 に答える 2

6

チャネル パイプラインはスレッド セーフですが、ここでの問題は、ダウンストリーム イベントとアップストリーム イベントでイベント実行モデルが異なることです。

  • ダウンストリーム ハンドラーは、デフォルトで (複数の) ユーザー スレッドを使用して実行されます。

  • ダウンストリーム ハンドラーは、複数のユーザー スレッドで任意の順序で実行できるため、既定ではスレッド セーフではありません (通常DownstreamEventsは軽量であるため、ハンドラーはインスタンス変数の状態を維持しません)。OneToOneEncoderNetty コード ベースの実装をご覧ください。それらのどれも状態を維持しません。

  • アップストリーム ハンドラーは、既定では単一のスレッドを使用して実行されるか、複数のスレッドを 1 つずつ使用して実行されます (実行ハンドラーが使用されている場合)。

  • アップストリーム ハンドラーは、シングル スレッド イベント実行のため (変更可能な状態を保持できる場合でも) スレッド セーフです。

そのため、ダウンストリーム ハンドラーがアップストリーム ハンドラーと同じくらいスレッド セーフであると誤解する人がいる可能性があります。

あなたが言ったように、状態が必要ない場合、解決策はインスタンス変数をローカルスコープに移動することです。それ以外の場合は、下流の処理メソッドをスレッド セーフにします。

于 2012-05-22T12:39:29.630 に答える
0

並行性を正しく理解していると思います。次のいずれかを行う必要があります。

  1. チャネル ハンドラがスレッド セーフであることを確認します (インスタンス変数の変更はありません)。
  2. ChannelPipelineFactory を使用して、チャネルごとに新しいパイプラインが作成されるようにします。
于 2012-05-22T11:58:26.153 に答える