0

そのため、私のマルチスレッド プログラムは、gzip が正常に解凍できるものを出力できず、他の問題が発生する可能性があることを前置きします。しかし、私が気付いたのは、シングルスレッドとマルチスレッドの各ブロックの圧縮サイズが根本的に異なるということです。

私のシングルスレッド実行では、SYNC_FLUSH が設定された GZIPOutputStream(System.out, true) があります。バッファがいっぱいになるまで、system.in から継続的に読み取ります。

GZIPOutputStream compressor = new GZIPOutputStream(System.out, true);
bytesRead = inBytes.read(buff,0,BLOCK_SIZE);
....
while(bytesRead != -1)
{
    offset += bytesRead;
    if (offset == BLOCK_SIZE)
    {
        compressor.write(buff,0,offset);
        compressor.flush();
        offset = 0;
    }

    if((bytesRead=inBytes.read(buff,offset,BLOCK_SIZE-offset)) == -1) {
        compressor.write(buff,0,offset);
        compressor.finish();
    }
}
compressor.close();

ご覧のとおり、バッファーがいっぱいになった後、コンプレッサに出力への書き込みを指示し、次にフラッシュを呼び出します。は、残りの出力を強制的に圧縮してフラッシュすることを保証するため、再度書き込みを行うときに、バッファーにデータが残っていません。

したがって、元の入力が最初からその長さであったかのように非常に似ています (したがって、各ブロックは独自の個別のストリームです)。

したがって、私のマルチスレッド プログラムでは、1 つの GZIPOutputStream の書き込みとフラッシュを行う代わりに、それぞれが独自の GZIPOutputStream を持つ一連のスレッドを使用するだけです。基本的に、その部分をスレッドへの呼び出しに置き換えます

List<Future<byte[]>> results = new ArrayList<Future<byte[]>>();
bytesRead = inBytes.read(buff,0,BLOCK_SIZE);

while(bytesRead != -1)
{
    offset += bytesRead;
    if (offset == BLOCK_SIZE)
    {
        results.add(exec.submit(new workerThread(buff,offset)));
        offset = 0;
    }

    if((bytesRead=inBytes.read(buff,offset,BLOCK_SIZE-offset)) == -1) {
        results.add(exec.submit(new workerThread(buff,offset)));
    }
}

圧縮するためにバッファを渡すだけです。私のスレッドが行うことはすべて

private ByteArrayOutputStream bOut = new ByteArrayOutputStream();
private byte[] finalOut;
....
public byte[] call() {
    try{
        GZIPOutputStream compress = new GZIPOutputStream (bOut, true);
        compress.write(input,0,size);
        compress.flush();
        compress.close();
     }
     catch (IOException e)
     {
        e.printStackTrace();
        System.exit(-1);
     }
     finalOut = bOut.toByteArray();
     return finalOut;
 }

私が文字通り行ったことは、スレッドに圧縮ジョブを与えたことだけだと思います。私は他に何も変更しませんでした。しかし、マルチスレッド プログラムを実行して結果を 16 進ダンプすると、通常、2 つのプログラム間で各ブロックが大きく異なることに気付きました。小さなバッファと小さな入力を使用したので、読みやすくなりました。

マルチスレッド プログラムで crc エラーが発生しました。これは、少なくとも gzip が形式を認識し、解凍を開始したことを意味します。それが完了したとき、最終結果がCRCから期待されたものと一致しないというだけです(解凍された出力のサイズなど)。

正直なところ、なぜこれが起こるのかわかりません。もっと明白なエラーを期待していたでしょうが、これは非常にランダムに見えます。確実に圧縮しています。そして、シングルスレッドプログラムとマルチスレッドプログラムの間の最初の数バイト (もちろんヘッダーの後) はしばしば同じであるため、順不同で連結しているとは思いません (さらに、executor.get() 関数はそれを処理する必要があります)。 .

私はただ困惑しています。gzip は連結されたストリームを解凍できることを知っています。入力を文字通り半分に分割して個別に出力し、それらをシングルスレッド プログラムで結合すると、問題なく解凍されました。

記録のために、328 個の「A」文字を含むファイルで試しただけなので、それほど大きくはありません。単一スレッドの GZIPOutputStream の 16 進ダンプは次のとおりです。

0000000 8b1f 0008 0000 0000 0000 7472 581c 0000
0000010 0000 ffff 681a 0004 0000 ffff 21a2 02e2
0000020 0000 ff00 03ff a800 5bff 5c79 0001 0000

マルチスレッドの場合は

0000000 8b1f 0008 0000 0000 0000 7472 19a4 22e0
0000010 1146 0000 ff00 03ff 7500 5f6c 80d1 0000
0000020 1f00 088b 0000 0000 0000 a200 e221 4622
0000030 0011 0000 ffff 0003 6c75 d15f 0080 0000
0000040 8b1f 0008 0000 0000 0000 21a2 02e2 0000
0000050 ff00 03ff 8a00 193b 5c21 0000 0000     

彼らはかなり違います。

うわー、これは本当に長くなりました。申し訳ありません。本当に困惑して立ち往生しました。

4

2 に答える 2

1

flush()とのfinish()呼び出しは必要ありません。 close()終了し、flush()呼び出しは不要な空のブロックを deflate ストリームに追加するだけです。は必要ないのでflush()、true を設定する必要はありませsyncFlushん。何もしないからです。

もちろん、1 つの大きな gzip ストリームを作成する場合と、小さな gzip ストリームを多数作成する場合では、まったく異なる結果が得られます。すべての gzip ストリームには、ヘッダーとトレーラー用に 18 バイトのオーバーヘッドがあります。使用している小さなブロックでは、そのオーバーヘッドが結果を完全に支配します。

スレッド化された例には大きなバグがあります。スレッド化されていない例では 328 の「A」が圧縮されていましたが、スレッド化された例では「A」と改行文字 (10 進数の 10) が混在しています。おそらく、圧縮しようとさえせずに開始し、いくつかの入力 (同一の文字のシーケンスだけでなく、実際のテキスト) を分割し、チャンクをスレッドに送信し、スレッドがデータに対して何もしないようにするかどうかを確認する必要があります。次に、元の入力を正しく再構築します。それができたら、戻ってきてください。

于 2012-10-31T06:06:23.840 に答える
0

どこかから始めるには:

彼らはかなり違います。

コメントからの私の仮定が成り立つ場合 (gzip は、2 つの文字列 a、b に対して unzip(gzip(a+b))=unzip(gzip(a) + gzip(b)) を満たす)、これは予想される動作です。

RFCによると、すべての gzip 呼び出しでヘッダーが書き込まれます。Javaの場合:

private void writeHeader() throws IOException {
    out.write(new byte[] {
                  (byte) GZIP_MAGIC,        // Magic number (short)
                  (byte)(GZIP_MAGIC >> 8),  // Magic number (short)
                  Deflater.DEFLATED,        // Compression method (CM)
                  0,                        // Flags (FLG)
                  0,                        // Modification time MTIME (int)
                  0,                        // Modification time MTIME (int)
                  0,                        // Modification time MTIME (int)
                  0,                        // Modification time MTIME (int)
                  0,                        // Extra flags (XFLG)
                  0                         // Operating system (OS)
              });
}

GZIP_MAGIC は 8b1f です:

private final static int GZIP_MAGIC = 0x8b1f;

そして Deflater.DEFLATED は 8 です:

public static final int DEFLATED = 8;

ヘッダーは次のように始まり
1f 8b 80 ...
ます: 出力でこの部分を明確に確認できます (バイト スワップ)。ヘッダーは、新しい gzip パーツごとに再度開始されます。したがって、チャンクされた出力は、通常の出力よりも長くする必要があります。

マルチスレッドの問題について: 何が起こっているのかを確認するには、完全なサンプルが必要です。

于 2012-10-31T01:25:34.483 に答える