1

おわかりのように、私はマルチスレッドが初めてで、ここで少し立ち往生しています。私のプログラムPchangeThreadでは、プログラムの実行中の任意の時点で別のスレッドからオンとオフを切り替えることができるスレッド (以下の例) が必要です。pixelDetectorOn()スレッドは開始時に一時停止し、が呼び出されたときに再開する必要があります。

ほとんどの場合、2 つのスレッドは、開始/停止フラグを除いてデータを共有する必要はありません。念のため、とにかくメインスレッドへの参照を含めました。

ただし、以下のコードで出力される唯一のメッセージは「ループに入る前」です。これは、何らかの理由でスレッドが wait() から復帰しないことを示しています。これはある種のロックの問題だと推測していますが、正確に何が問題なのかを理解できていません。this.detectorメインスレッドからロックしても同じ結果が得られます。また、wait()/notify()パラダイムが本当にスレッドの中断と起動に適しているかどうか疑問に思っています。

public class PchangeThread extends Thread {
  Automation _automation;
  private volatile boolean threadInterrupted;

  PchangeThread(Automation automation)
  {
    this._automation = automation;
    this.threadInterrupted = true;
  }

  @Override
  public void run()
  {
    while (true) {
      synchronized (this) {
        System.out.println("before entering loop");
        while (threadInterrupted == true) {
          try {
            wait();
            System.out.println("after wait");
          } catch (InterruptedException ex) {
            System.out.println("thread2: caught interrupt!");
          }
        }
      }
      process();
    }
  }

  private void process()
  {
    System.out.println("thread is running!");

  }

  public boolean isThreadInterrupted()
  {
    return threadInterrupted;
  }

  public synchronized void resumeThread()
  {
    this.threadInterrupted = false;
    notify();
  }
}

resumeThread()次の方法でメインスレッドから呼び出されます。

public synchronized void pixelDetectorOn(Context stateInformation) {        
        this.detector.resumeThread();
}

detectorのインスタンスへの参照ですPchangeThread。「検出」スレッドは、プログラムのメイン モジュールで次のようにインスタンス化されます。

detector=new PchangeThread(this);
4

4 に答える 4

3

あなたが言ったように、共有フラグへのアクセスを保護する必要があります。揮発性を宣言しthreadInterruptedましたが、まだ同期を使用しています。必要なのは 1 つだけです。物事が簡単になるので、私は単に同期化を使用することを好みます。マルチスレッドは非常に複雑です。より複雑なコントロールが必要であることがわかっている場合を除き、シンプルにしておいてください。これは、threadInterrupted読み取りまたは書き込みが行われるたびに、アクセスを同期する必要があることを意味します。setThreadInterrupt()現在、とではそれを行っていませんisThreadInterrupted()

次に、できるだけ小さなコード ブロックで同期する必要があります。の内部ではrun()、内側のループを介して同期しています。実際には、 の読み取り時に同期するだけで済みますthreadInterrupted。の実装がisThreadInterrupted()上記のように修正されると、それを直接使用して、同期ブロックを内側のループから削除できます。

内側のループで同期しているという事実は、コードが「スレッドが実行中です!」と出力しない原因となっているエラーです。PchangeThread自身のロックを取得しwait()、スレッドを中断するために呼び出します。ただし、スレッドはこの時点でまだロックを保持しています。後で、スレッドresumeThread()を再起動するためにメイン スレッドが呼び出されます。ただし、そのメソッドは最初にロックの取得を待機する必要があるため、実行を開始できません。ただし、PchangeThread通知されるまでロックを取得することはありません。

を設定する 2 つの方法を提供しthreadInterruptedていますが、値が false に設定されたときにスレッドに通知するのはそのうちの 1 つだけです。本当に必要setThreadInterrupt()ですか?私はあなたがしないと思います。そのままにしておくと、resumeThread()引数が false の場合と同じように動作するはずです。

最後に、インスタンス自体ではなくプライベート オブジェクトをロックすることをお勧めします。プライベート ロック オブジェクトを完全に制御できます。ただし、スレッド インスタンスへの参照を持っている人は、それを同期ブロックのロックとして使用することもでき、見つけにくいデッドロックにつながる可能性があります。

私の編集を使用するように変更されたコード:

public class PchangeThread extends Thread {
  private final Object _lock = new Object();
  Automation _automation;
  private final boolean _threadInterrupted;

  PchangeThread(Automation automation)
  {
    _automation = automation;
    _threadInterrupted = true;
  }

  @Override
  public void run()
  {
    while (true) {
      System.out.println("before entering loop");
      while (isThreadInterrupted()) {
        try {
          wait();
          System.out.println("after wait");
        } catch (InterruptedException ex) {
          System.out.println("thread2: caught interrupt!");
        }
      }
      process();
    }
  }

  private void process()
  {
    System.out.println("thread is running!");

  }

  public boolean isThreadInterrupted()
  {
    synchronized (_lock) {
      return _threadInterrupted;
    }
  }

  public void resumeThread()
  {
    synchronized (_lock) {
      _threadInterrupted = false;
      notify();
    }
  }
}
于 2012-09-09T04:12:32.047 に答える
1

この場合、私は個人的に次の質問を自問します。

中断された

メインスレッドによってのみフラグが設定されます。たとえば、ワーカースレッドはそれを読み取り、フラグに基づいて待機するかどうかを決定しますが、フラグは更新しません。または、メイン スレッドとワーカー スレッドの両方で設定できます。

前者の場合は、揮発性ブール値を使用してください。こうすることで、ワーカー スレッドは volatile の値をキャッシュせず、常にメモリから読み取ります。1 つのスレッド (メインのスレッド) だけが更新するため、競合状態は発生しません。パブリッシュ/サブスクライブのシナリオと考えてください。

シナリオが後者のカテゴリに該当する場合は、AtomicBoolean 変数を使用してください。どちらの場合も、ロックを取得しないため、synchronized キーワードよりも効率的ですが、Atomic* 変数の場合は、ロック取得よりも軽量なCAS操作を利用します。

于 2012-09-09T14:11:22.530 に答える
0

あなたのコードは間違っていません(理想的ではありませんが)。実行すると、予想されるすべてのメッセージが出力されます。おそらく、を呼び出さないだけですresumeThread()

いくつかのアドバイス:

  • スレッドで同期せず、ランナブルを作成して同期します。

  • 計算を開始したいのですが、計算するデータは何ですか?彼らは別の方法で行くように見えます。これはエラーの原因です。データと制御の両方に単一のチャネルを使用します。推奨される方法は、そのようなチャネルにキューを使用することです。たとえば、LinkedBlockingQueueはすでに適切な方法で同期されています。

于 2012-09-10T13:51:46.517 に答える
0

誰かがこれを読むとは思えませんが、誰かが知りたい場合に備えて:

デバッガーのログを確認したところ、奇妙なことに気付きました。「コンパイルできないソース コードでデバッグが停止しました: )Void;」と表示されていました。このエラーの原因となった可能性のあるソースは何も考えられなかったので、使用していた外部コードの一部に Netbeans に問題があると推測しました (これはブレークポイントが原因ではなく、プロジェクトは正常にコンパイルされました!) . そのため、使用しているサードパーティのライブラリを最新バージョンに更新しました。そして見よ:その後、resumeThread()! を呼び出したときに、突然 null ポインター例外が発生しました。コードの残りの部分をチェックしたところ、すぐにバグが見つかりました (実際、スレッドへの参照は null でした)。

要約すると、奇妙な動作は私のプログラムのマイナーなバグが原因でしたが、外部 jar の何かが原因で、スローされるはずの例外が抑制されました。好奇心から、jarをダウングレードしてバグを「修正解除」することで再確認しましたが、例外が飲み込まれ、上記の奇妙なメッセージでデバッガーが終了しました。

Netbeans バージョン 7.1.1

于 2012-09-11T23:42:31.783 に答える