1

私は単純な問題だと思っていましたが、まだ良い解決策を見つけていません: Swing インターフェイスパネルのボタンを押すことで、スレッドで行われているアクティビティを一時停止および再開できるようにしたいと考えています。

具体的には、1 つのスレッドを使用してオーディオ フレームをリアルタイムで取り込みたいと考えています。これらのフレームで魔法の処理を実行する 2 番目のスレッド。結果をシリアル化し、ソケットを介して別の場所に送信する 3 番目のスレッド。キッカーは、使用する魔法のブランドによっては、2 番目のスレッドでの処理が実際のデータ収集よりもフレームあたりの実行に時間がかかり、しばらくするとデータが蓄積される可能性があることです。

非常に大雑把なプロトタイプの回避策として、音声収集プロセスのオンとオフを切り替えるボタンとステータス バー (後で実装する予定) を備えた GUI を追加して、ユーザーが音声収集プロセスがどれだけいっぱいになっているかを監視できるようにしようと考えました。バッファー (リンクされたブロッキング キュー) がたまたま存在していました。

これは予想以上に大変です。問題をおもちゃのバージョンに落とし込みました: 50 個の整数を格納できるリンクされたブロッキング キュー、GUI、2 つのスレッド (異なる速度でキューに追加およびキューから削除)、およびブール値をラップする Token オブジェクト。それはこのように見えます、そしてそれはちょっとうまくいきます:

テスト.java

public class Test {
  public static void main(String[] args) throws IOException {

    Token t1 = new Token();
    Token t2 = new Token();
    LinkedBlockingQueue<Integer> lbq = new LinkedBlockingQueue<Integer>(50);

    startFill sf = new startFill(t1, lbq);
    startEmpty se = new startEmpty(t2, lbq);

    TestUI testUI = new TestUI(t1, t2, lbq);
    testUI.setVisible(true);

    sf.start();
    se.start();
  }
}

TestUI.java

public class TestUI extends JFrame implements ActionListener {
  private JToggleButton fillStatus, emptyStatus;
  public boolean filling, emptying;
  public Token t1, t2;
  public LinkedBlockingQueue<Integer> lbq;

  public TestUI(Token t1, Token t2, LinkedBlockingQueue<Integer> lbq) {
    this.t1 = t1;
    this.t2 = t2;
    this.lbq = lbq;
    initUI();
  }

  public synchronized void initUI() {
    JPanel panel = new JPanel();
    panel.setLayout(null);

    filling = false;
    fillStatus = new JToggleButton("Not Filling");
    fillStatus.setBounds(20, 20, 150, 25);
    fillStatus.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
        if (filling == false) {
          fillStatus.setText("Filling");
        } else {
          fillStatus.setText("Not Filling");
        }
        filling = !filling;
        t1.flip();
        System.out.println("fill button press");
      }
    });

// Similar code for actionListener on Empty button, omitted

    panel.add(fillStatus);
    panel.add(emptyStatus);
    add(panel);
    setTitle("Test interface");
    setSize(420, 300);
    setLocationByPlatform(true);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
  }

  public void actionPerformed(ActionEvent e) {
  }
}

startFill.java

public class startFill extends Thread {
  public Token token;
  public LinkedBlockingQueue<Integer> lbq;

  public startFill(Token token, LinkedBlockingQueue<Integer> lbq) {
    this.token = token;
    this.lbq = lbq;
  }

  public void run() {
    int count = 0;
    while (true) {
      while (!token.running()) {
        try {
          sleep(200);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }

      while (token.running()) {
        try {
          lbq.put(count);
          System.out.println("queue size = " + lbq.size());
          count++;
          sleep(100);
        } catch (InterruptedException e1) {
          e1.printStackTrace();
        }
      }
    }
  }
}

また、ほぼ同じように機能する startEmpty.java と、ブール状態変数のラッパーである Token.java もありますが、簡潔にするために省略されています。

これでうまくいきますが、while (!token.running())ループ内でのポーリングが犠牲になります。Locks と Conditions を使用しようとしましたが、失敗し、常に IllegalMonitorStateExceptions が発生しました。

そして、私はこの同様の質問を見て、なんとかそれを機能させることができましたが、Java 5 と Java 6 で明らかに大きく異なり、非常に落胆しているように見える yield() メソッドを使用することを犠牲にしました。

だから私の質問: 私がやろうとしていることを行うための正しい、またははるかに良い方法はありますか? ポーリングなしで信頼できる方法でこれを実現する方法があるはずです。

更新:アプリケーションで何らかの方法でオーディオ キャプチャ ループを制御する問題を回避できるかどうかはわかりません。ボタンを押すのが人間であろうと、他の要因に基づいて決定を下す内部ロジックであろうと、私たちは本当に、その忌まわしいものをシャットダウンしてコマンドで復活させることができる必要があります.

4

3 に答える 3

3

GUI を介して手動で 3 つのワーカー プロセス間の同期を処理する代わりに、ワーカー間のファクトリ ラインアップをセットアップすることもできます。

  • ワーカー間に 2 つのキューを追加します
  • キュー状態の条件でスレッドをブロックします。
    • リーダー (消費者) が空のキューでブロックする
    • ライター (プロデューサー) は、キューがいっぱいになるとブロックします (n がそのキューのコンシューマーの数である 2n メッセージとします)。
  • wait()キューでスレッドをブロックし、キューにnotifyAll()メッセージを追加またはキューから削除した後にそのキューで。

このようなセットアップは、消費者よりも速く実行されている生産者を自動的に遅くします。

于 2012-06-17T18:42:25.917 に答える
0

実装しない理由ArrayBlockingQueue.

スレッドセーフな java.util.concurrent パッケージにあるArrayBlockingQueueクラスを使用することをお勧めします。

BlockingQueue<String> queue = new ArrayBlockingQueue<String>(100);
于 2012-06-17T18:59:14.087 に答える
0

私がやろうとしていたことを行う1つの方法は次のとおりです。次のように、Tokenオブジェクトで同期されたwait()とnotify()を適切に使用します。

startFill.java run() メソッド

  public synchronized void run() {
    int count = 0;
    try {
      // token initializes false
      // wait until notification on button press
      synchronized (token) {
        token.wait();
      }

      // outer loop
      while (true) {
        // inner loop runs as long as token value is true
        // will change to false on button press
        while (token.running()) {
          lbq.put(count);
          System.out.println("queue size = " + lbq.size());
          count++;
          sleep(100);
        }

        // wait until notification on button press, again      
        synchronized (token) {
          token.wait();
        }
      }
    } catch (InterruptedException e2) {
      e2.printStackTrace();
    }
  }

TestUI.java アクションリスナー:

fillStatus.addActionListener(new ActionListener() {
  // t1 was initialized false
  public void actionPerformed(ActionEvent event) {
    if (filling == false) {
      fillStatus.setText("Filling");
      // if false, change t1 status to true
      t1.flip();
      // and send the notification to the startFill thread that it has changed
      synchronized (t1) {
        t1.notify();
      }
    } else {
      fillStatus.setText("Not Filling");
      // if true, change t1 status to false
      t1.flip();
      // no notification required due to polling nature of startFill's active thread
    }
    filling = !filling;
    System.out.println("fill button press");
  }
});

これは、スレッドがオフになっている間はポーリングせずに、かなりうまく機能します。

私の最初の試みは、構文が悪いために失敗しました。wait synchronized (token) {...}() ステートメントと notify() ステートメントの周りのコンテキスト ブロックを無視しました。

于 2012-06-18T03:49:35.810 に答える