2017-10-09 2 views
1

私は以下のように、オブジェクトからファイルに内容を書き込む際に1つのメソッドを呼び出す複数のスレッドを持っています。 このメソッドをテストするために1つのスレッドを使用すると、ファイルへの出力が期待されます。しかし、複数のスレッドの場合、ファイルへの出力は面倒です。どのようにこのスレッドを安全にするには?スレッドメソッドをスレッドセーフにするには?

void (Document doc, BufferedWriter writer){ 
     Map<Sentence, Set<Matrix>> matrix = doc.getMatrix(); 
     for(Sentence sentence : matrix.keySet()){ 
      Set<Matrix> set = doc.getMatrix(sentence); 
      for(Matrix matrix : set){ 
       List<Result> results = ResultGenerator.getResult(); 
       writer.write(matrix, matrix.frequency()); 
       writer.write(results.toString()); 
       writer.write("\n"); 
      } 
     } 
} 

編集:

私は、この行List<Result> results = ResultGenerator.getResult()を追加しました。私が本当に望むのは、このメソッド呼び出しを処理するために複数のスレッドを使用することです。なぜなら、この部分は高価で時間がかかるからです。書く部分は非常に速く、私は本当に複数のスレッドを必要としません。

この変更が与えられた場合、このメソッドを同時環境で安全に呼び出す方法はありますか?

+0

これを 'synchronized'にすることはできますが、ロジックを再考する必要があります。あなたは_同じスレッドに複数のスレッドを書く必要がありますか? – Gabriel

+5

同じ出力先への書き込みは、本質的に安全ではありません。なぜあなたは助けになる必要があると思うかを説明します。 1つのリーダーで並行キューに文書をポストするようなことをするのが有力な解決策です。 – chrylis

+0

@Gabriel、私の '編集'を見て、あなたの提案を提供してください。 – user697911

答えて

1

私はJavaに精通していないので、私は言語にとらわれない答えを提供するつもりです。

あなたがしたいのは、行列を結果に変換し、それらを文字列としてフォーマットし、すべてをストリームに書き込むことです。

現在、各結果を処理するとすぐにストリームに書き込んでいます。したがって、ロジックにマルチスレッドを追加すると、ストリームに競合状態が発生します。

あなたはすでにResultGenerator.getResult()のコールだけを並行して実行する必要がありますが、ストリームは引き続き順次アクセスする必要があることを認識しました。

これを実際に置くだけで済みます。順番にそれを実行します。

  • は、各項目は、あなたがこのように(これはmap操作です)すべての結果を生成する結果
  • プロセスに並行して、このリストを生成するために必要なものであるリストを作成します。あなたのリストは結果のリストになります。
  • これで、すでに結果が得られているため、順番に繰り返してストリームに書式を設定して書き込むことができます。

私は、Java 8が機能的な方法ですべてを実現するツールを提供していると思うが、私はJavaの人ではないと言っているので、コードサンプルを提供することはできない。私はこの説明が十分であることを願っています。

@edit

F#でこのサンプルコードは、私が何を意味するのかを説明します。

open System 

// This is a pretty long and nasty operation! 
let getResult doc = 
    Threading.Thread.Sleep(1000) 
    doc * 10 

// This is writing into stdout, but it could be a stream... 
let formatAndPrint = 
    printfn "Got result: %O" 

[<EntryPoint>] 
let main argv = 
    printfn "Starting..." 

    [| 1 .. 10 |] // A list with some docs to be processed 
    |> Array.Parallel.map getResult // Now that's doing the trick 
    |> Array.iter formatAndPrint 

    0 
-2

私はそれを同期させるでしょう。その場合、アプリケーション内の1つのスレッドだけが同時にこのメソッドを呼び出すことができます=> No messy出力。複数のアプリケーションを実行している場合、ファイルロックのようなものを考慮する必要があります。同期方法

例:

public synchronized void myMethod() { 
    // ... 
} 

この方法では、スレッドごとに排他的です。

-2

メソッドをロックし、終了したらメソッドをロック解除することができます。メソッドの前にsynchronizedを置くことで、一度に1つのスレッドしか実行できないことを確認します。同期化はJavaの処理速度を低下させるため、必要なときにのみ使用してください。

ReentrantLock lock = new ReentrantLock(); 

/* synchronized */ 
public void run(){ 

    lock.lock(); 

    System.out.print("Hello!"); 

    lock.unlock(); 

} 

これは、synchronizedのようにメソッドをロックダウンします。あなたは同期の代わりにそれを使うことができます。そのため、同期は上でコメントアウトされています。

1

最終的なファイルが所定の順序で必要な場合は、マルチスレッド化しないでください。そうしないと、期待通りの結果が得られません。

マルチスレッドでは、プログラムがI/O出力に関して高速に実行されると思われる場合は、誤っている可能性があります。同期のためにロックやオーバーヘッドが発生するため、実際には単一のスレッドよりパフォーマンスが低下します。

非常に大きなファイルを書き込もうとすると、Documentのインスタンスの順序は関係なく、ライターの方法がCPUのボトルネックになると思います(私のコードからわかる唯一の原因は、 frequency()メソッド呼び出し)では、各スレッドが一時ファイルに書き込む独自のBufferedWriterを保持してから、すべてを待機するスレッドを追加して、連結を使用して最終ファイルを生成します。

2

基本的に、最後に1つのファイルで制限されています。グローバル変数はなく、何も公開しないので、メソッドはスレッドセーフです。 処理に時間がかかる場合は、並列ストリームを使用して、結果をconcurrenthashmapまたはブロッキングキューに公開できます。ただし、ファイルに書き込むコンシューマーは1つだけです。

+0

同じライターを複数の呼び出しに渡すと、このメソッドは安全ではありません。 – chrylis

+0

そうですが、それは本質的に安全ではありません。私がここで暗示しているのは、結果をブロッキングキューにパブリッシュし、単一のコンシューマがファイルに書き込む必要があるということです。また、メソッド定義の中には、ライターがb/w呼び出しを共有していると仮定していないものもあります。 –

+0

いいえ、問題の説明に加えてライターを渡すことは大きな赤旗です。 – chrylis

0

コードで個別のdocオブジェクトとwriterオブジェクトを使用している場合は、インスタンス変数にアクセスして使用しないため、メソッドはすでにスレッドセーフです。

メソッドに同じライターオブジェクトを渡す書いている場合は、あなたのニーズに応じて、これらのアプローチのいずれかを使用できます。一時的なStringBuilderオブジェクトを使用して

void (Document doc, BufferedWriter writer){ 
     Map<Sentence, Set<Matrix>> matrix = doc.getMatrix(); 
     for(Sentence sentence : matrix.keySet()){ 
      Set<Matrix> set = doc.getMatrix(sentence); 
      for(Matrix matrix : set){ 
       List<Result> results = ResultGenerator.getResult(); 

       // ensure that no other thread interferes while the following 
       // three .write() statements are executed. 
       synchronized(writer) { 
        writer.write(matrix, matrix.frequency()); // from your example, but I doubt it compiles 
        writer.write(results.toString()); 
        writer.write("\n"); 
       } 
      } 
     } 
} 

またはロックフリーを:

void (Document doc, BufferedWriter writer){ 
     Map<Sentence, Set<Matrix>> matrix = doc.getMatrix(); 
     StringBuilder sb = new StringBuilder(); 
     for(Sentence sentence : matrix.keySet()){ 
      Set<Matrix> set = doc.getMatrix(sentence); 
      for(Matrix matrix : set){ 
       List<Result> results = ResultGenerator.getResult(); 
       sb.append(matrix).append(matrix.frequency()); 
       sb.append(results.toString()); 
       sb.append("n"); 
      } 
     } 
     // write everything at once 
     writer.write(sb.toString(); 
} 
関連する問題