1

ファイルに複数回 (100k+) 書き込みをしようとしていますが、書き込みは不安定なネットワーク経由で行われます。これを行うために、JavaExecutorServiceを使用してスレッドを生成することを検討していますが、どの設定の組み合わせが次のことを適切に行うのかよくわかりません。

  1. 一度に1つの書き込みのみを許可します(もちろん順序は重要です)
  2. 書き込みが各書き込みを実行するのに十分な時間 (たとえば 5 秒) を許可し、その時点で保釈します。
  3. 書き込みが遅い場合は、Executor に書き込みをキューに集めて待機させます。
  4. スレッド キューが空になるまで、プログラム全体を終了させないでください。
  5. ライターごとにスレッドを分けます。つまり、まったく同じライターがこの関数に入っている場合は、それを独自のキューに入れます。別のライター ポインターが入ってきた場合は、独自のキューを指定します (別のライターを同じキューに入れる必要はありません)。

これは、エグゼキューター機能とメイン プログラムのオブジェクトの.wait()andコマンドを組み合わせることで実現できると思います。.notify()ただし、エグゼキューター API を正確に操作してこれを行う方法がよくわかりません。

これが私が得たものです:

private void writeToFileInSeperateThread(final PrintWriter writer, final String text) {
  ExecutorService executor = Executors.newSingleThreadExecutor();
  try {
    executor.submit(new Thread(new Runnable() {
      public void run() {
        writer.println(text);
      }
    })).get(5L, TimeUnit.SECONDS);
  } catch (Exception e) {
    e.printStackTrace();
  }
  executor.shutdown();
}

そのメソッドは、1 つのプロセスで 10 万回以上呼び出されるため、ExcutorService毎回新しいインスタンスを作成する必要があるのか​​、同じインスタンスを利用する必要があるのか​​ わかりません。(同じものを利用しようとして、.newSingleThreadExecutor()ディレクティブに関連していると思われる例外が発生し続けました。

Java 5 準拠を維持したいが、Java 6 は問題ない。Windows XP/7 で動作します。

更新: これは初期テストでうまくいったようです:

  private class WriterStringPair {
    public final PrintWriter writer;
    public final String text;

    public WriterStringPair(PrintWriter writer, String text) {
      this.writer = writer;
      this.text = text;
    }
  }

  private void writeTextInSeperateThread(Writer writer, String text) {
    try {
      textQueue.offer(new WriterStringPair(writer, text), 300L, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
      errOut.println(e);
      e.printStackTrace();
    }
  }

  final BlockingQueue<WriterStringPair> textQueue = new ArrayBlockingQueue<WriterStringPair>(500);

  private void setWritingThread() {
    new Thread((new Runnable() {
      public void run() {
        WriterStringPair q;
        while (!shutdown && !Thread.currentThread().isInterrupted()) {
          try {
            q = textQueue.poll(1L, TimeUnit.SECONDS);
            if (q != null) {
              q.writer.write(q.text + "\n");
              q.writer.flush();
            }
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }
    })).start();
  }
4

2 に答える 2

3

「不安定な」ネットワークを介したファイルの書き込みとその意味についての詳細を知らずに、詳細を提供することは困難です. しかし、ここで考えるべきことがいくつかあります。

ここで最高のパフォーマンスが得られる同時ライターの数、または宛先で最も信頼できる出力が得られる数を把握します。次に、一定数のこれらのライターを開始する必要があります。それぞれが共有から消費しBlockingQueueます (重要な場合は、ライターごとに 1 つのキュー)。IO またはネットワーク帯域幅をすぐに超える必要があるため、5 程度のライターから始めて、必要に応じて増減する必要があります。

public void run() {
   writer.println(text);
}

ええ、1行あたりのジョブという点では、このようなことはしたくありません。を に入れ、String textライターBlockingQueue<String>クラスRunnableExecutorServiceそのキューからのデキューで実行し、キューが空であるかshutdownブール値が設定されている場合にのみ停止する方がよいでしょう。

Peter が言及しているように、キューに入れられたテキスト文字列でメモリがいっぱいになることに注意する必要があります。入力テキストが大きい場合はBlockingQueue、数百程度に制限を設定する必要があります。

ExecutorService毎回新しいインスタンスを作成する必要があるのか​​、それとも同じインスタンスを使用する必要があるのか​​わかりません。

確かに、単一のサービスを用意し、何度も作成しないようにする必要があります。

これは、エグゼキューター機能と、メイン プログラムのオブジェクトに対する .wait() および .notify() コマンドの組み合わせで実行できると思います。

これを正しく記述すれば、wait と notify を使用する必要はありません。私volatile boolean shutdown = falseはあなたのすべての作家が見ていると思います。それらのそれぞれは、シャットダウンを見て、テキスト キューからデキューします。何かのようなもの:

while (!shutdown && !Thread.currentThread().isInterrupgted()) {
    String text = textQueue.poll(1, TimeUnit.SECONDS);
    if (text != null) {
        // write the text
    }
}

書き込みが失敗した場合、または何かが必要な場合は、再試行できます。

于 2013-07-17T18:31:41.380 に答える
2

いくつかの問題

  • println は IOException が発生したかどうかを通知しないため、エラーからの保護が必要な場合、これは役に立ちません。
  • 各行の ExecutorService の開始は非常に遅く、タスクを送信するよりもはるかに遅くなります。
  • 多くのタスクを作成すると非常に遅くなるだけでなく、メモリをすべて使い果たしてしまう可能性があります。
  • Threads ではなく Runnable を ExecutorService に送信します
  • たとえば、書き込み時にスレッドがブロックされている場合、シャットダウンはスレッドを停止しません。これにより、多くのスレッドが同時に書き込もうとして放置される可能性があります。

データを JMS やデータベースやファイル (Java-Chronicle など) などのローカル システムに保存し、データが利用可能になったら別のプロセスで NFS にコピーすることをお勧めします。

これは、NFS を修正できないと想定しているため、不安定ではありません。

于 2013-07-17T18:32:15.047 に答える