0

アプリの I/O の速度を上げようとしているので、複数のスレッドを使用して保存することにしました。ファイルは book/symbol/file という階層で構造化されており、複数のスレッドが同じディレクトリに複数のファイルを同時に保存する可能性があります。すべてのファイルを順番に保存すると問題ありません。ただし、複数のスレッドが起動すると、ファイルの形式が正しくなく、そのファイルをロードすると「IOException : 無効なブロック」が発生することがあります。この場合、同時実行が問題を引き起こす可能性がある理由はありますか?

以下のコード:

    private void storeAppendingTimestamps(Series timeSeries) throws MetricPersistException {
        Metric metric = timeSeries.getMetric();
        Path outPutFile;
        try {
            outPutFile = generateOutputFilePath(metric);
            if (!Files.exists(outPutFile)) {
                createNewFile(outPutFile);
            }
        } catch (IOException e) {
            throw new PersistException("Cannot create output file for metric " + metric);
        }
        try (PrintWriter writer = new PrintWriter(new GZIPOutputStream(new FileOutputStream(outPutFile.toFile(), true)), true)) {
            for (SeriesDataPoint dataPoint : timeSeries.getTimeSeriesPoints()) {
                writer.println(String.format("%d %s", dataPoint.getTimestamp().getMillis(), formatPlain(dataPoint.getValue())));
            }
            writer.close();
        } catch (IOException e) {
            throw new MetricPersistException(String.format("IO Exception has occured while persisting metric %s: %s", metric, e.getMessage()));
        }
    }

そして、作業を分割するコード:

private void persistTimeSeries(Collection<Series> allSeries, CompletionService<Void> executorService) throws MetricPersistException {
        final LoggingCounter counter = new LoggingCounter(logger, "metric series file", 10000);
        for (final MetricTimeSeries series : allSeries) {
            executorService.submit(new Callable<Void>() {
                @Override
                public Void call() throws Exception {
                    persister.persistTimeSeries(series);
                    counter.increment();
                    return null;
                }
            });
        }
            for (int i = 0; i < allSeries.size(); i++) {
                Future<Void> future = executorService.take();
                future.get();
            }
            counter.finish();
        }
4

1 に答える 1

1

パフォーマンスを求めている場合は、とのBufferedOutputStream間にを導入することで、ほぼ確実にパフォーマンスが大幅に向上します。GZIPOutputStramFileOutputStream

その際、正しいエンコーディング仕様の を追加しOutputStreamWriterて、特定のマシンで実行していない人がファイルを適切に解釈できるようにします。


私が見た明らかにスレッドセーフでないコードの 1 つは次のstoreAppendingTimestamps()とおりです。同じ出力ファイルにマップされる複数のシリーズがある場合、それらの両方が同じファイルを開いて書き込むという競合状態が発生します (競合状態もあります)。ファイルの作成時ですが、それは冪等な操作だと思います)。

複数のシリーズが同じファイル名にマップされる可能性が高い場合は、スレッドセーフ/レースセーフガードが必要です。ConcurrentHashMap処理しているファイルの名前を保存するために使用される のようなもの。衝突が発生した場合は、(警告を出して) タスクを終了します。

于 2013-01-04T15:48:44.753 に答える