9

テキスト内の行数、単語数、文字数を数えるプログラムを作成しました。これはスレッドを使用して行います。うまく機能することもありますが、そうでないときもあります。最終的に起こるのは、カウントされた単語と文字の数を指す変数が不足する場合とそうでない場合があります。

必要なすべての単語または文字を数えることができる前に、スレッドが終了することがあるように私には思えます。while (true) ループが壊れると、これらのスレッドが範囲外になるためですか?

以下に、問題のスレッド部分のコードを含めました。

private void countText() {
  try {
    reader = new BufferedReader(new FileReader("this.txt"));
    while (true) {
      final String line = reader.readLine();
      if(line == null) {break;}
      lines++;
      new Thread(new Runnable() {public void run() {chars += characterCounter(line);}}).start();
      new Thread(new Runnable() {public void run() {words += wordCounter(line);}}).start();
      println(line);
    }

  } catch(IOException ex) {return;}

}

(サブ質問: 何かについて尋ねてコードを投稿したのはこれが初めてです。Google やウィキペディアの代わりに StackOverflow を使用したくなく、これが適切な質問ではないのではないかと心配していますか?より一般的な質問なので、コードの助けを求めているだけではありません...しかし、この種の質問がより適切な別のWebサイトはありますか?)

4

3 に答える 3

7

別のスレッド化された設計を使用すると、この種の問題を見つけて修正しやすくなり、より効率的に掘り下げることができます。長文の回答ですが、要約すると「Javaでスレッドをやっているなら、できるだけ早くjava.util.concurrentを調べてください」です。

このコードをマルチスレッド化して、単語のカウントを高速化するのではなく、スレッドを学習していると思いますが、それはスレッドを使用する非常に非効率的な方法です。行ごとに 2 つのスレッドを作成しています。つまり、1,000 行のファイルに対して 2,000 のスレッドです。(最新の JVM で) スレッドを作成すると、オペレーティング システムのリソースが使用され、一般にかなりのコストがかかります。2 つのスレッド (2000 どころか 2 つのスレッド) が共有リソース (charsおよびwordsカウンターなど) にアクセスする必要がある場合、結果として生じるメモリの競合によってパフォーマンスも低下します。

Chris Kimpton が示唆するように、またはWMR が示唆するようにカウンタ変数synchronizedを作成すると、おそらくコードが修正されますが、競合の影響もさらに悪化します。シングルスレッドアルゴリズムよりも遅くなると確信しています。Atomic

新しい番号を追加するたびにジョブをサブミットするワーク キューを備えた、charsとの両方を監視する長寿命のスレッドを 1 つだけ用意することをお勧めします。wordsこの方法では、1 つのスレッドだけが各変数に書き込みます。設計に変更を加えると、誰が何を担当しているかがより明確になります。また、メモリの競合がなく、タイトなループで何百ものスレッドを作成していないため、高速になります。

また、ファイル内のすべての行を読み取ったら、カウンターの値を実際に出力する前に、すべてのスレッドが終了するのを待つことも重要です。そうしないと、まだ終了していないスレッドからの更新が失われます。現在の設計では、作成したスレッドの大きなリストを作成し、最後にそれを実行して、それらがすべて停止していることを確認する必要があります。キューとワーカー スレッドの設計では、各スレッドにそのキューを空にするように指示し、それが完了するまで待つことができます。

Java (1.5 以降) では、この種の設計を非常に簡単に実装できます: java.util.concurrent.Executors.newSingleThreadExecutorを確認してください。また、単一のスレッドではなくスレッドプールに切り替えることができるため、後で同時実行性を簡単に追加できます (適切なロックなどを前提としています)。

于 2008-11-14T11:49:03.323 に答える
4

Chris Kimpton がすでに正しく指摘しているように、異なるスレッドの更新に問題がありcharsますwords。異なるスレッドが異なるオブジェクトで同期することを意味する現在のスレッドへの参照であるため、同期も機能しthisません。this同期できる追加の「ロック オブジェクト」を使用できますが、これを修正する最も簡単な方法は、おそらく2 つのカウンターにAtomicIntegersを使用することです。

AtomicInteger chars = new AtomicInteger();
...
new Thread(new Runnable() {public void run() { chars.addAndGet(characterCounter(line));}}).start();
...

これでおそらく問題は解決しますが、Sam Stoke のより詳細な回答は完全に正しいです。元の設計は非常に非効率的です。

スレッドがいつ「範囲外」になるかについての質問に答えるには: ファイル内のすべての行に対して 2 つの新しいスレッドを開始していて、それらのすべてがrun()メソッドの最後に到達するまで実行されます。これは、それらをデーモン スレッドにしない限りです) 。その場合、この JVM でまだ実行されているのがデーモン スレッドだけになるとすぐに終了します。

于 2008-11-14T11:37:31.303 に答える
3

私には良い質問のように聞こえます...問題は、文字 += と単語 += の原子性に関連している可能性があると思います-複数のスレッドが同時にそれを呼び出している可能性があります-存在することを確認するために何かしますか?インターリーブなし。

あれは:

スレッド 1、chars = 10、5 を追加したい

スレッド 2、chars = 10、3 を追加したい

スレッド 1 は新しい合計 15 を計算します

スレッド 2 は新しい合計 13 を計算します

スレッド 1 は chars を 15 に設定します

スレッド 2 は chars を 13 に設定します。

これらの変数を更新するときに同期を使用しない限り、可能かもしれません。

于 2008-11-14T11:17:29.177 に答える