1

皆さん、私の問題は、スレッドが 3 つあることです。

1スレッド(Bot1)

public class Bot1 implements Runnable {
  String name;

  public Bot1(String s) throws Exception{
    ChatterBotFactory factory = new ChatterBotFactory();
    ChatterBot bot1 = factory.create(ChatterBotType.CLEVERBOT);
    ChatterBotSession bot1session = bot1.createSession();
    name=s;
    name=bot1session.think(s);  
  }

  public void run(){
    System.out.println("b1: "+name);
  }
}

そして他は同じです。名前だけがBot2Bot3です。しかし、コードはほとんど同じです。これらのボットを同時に開始する必要があります。そして、最速の文だけを表示する必要があります。例: "Hello" が and よりも速く表示される場合はBot1、andスレッドを閉じる必要があります。しかし、どちらが速かったかをどのように確認できますか? また、コードを閉じて再度実行するには、どの 2 つが必要ですか? あなたが私を理解し、助けてくれることを願っています。ありがとう、そして私の下手な英語でごめんなさい。Bot2Bot3Bot2Bot3

4

5 に答える 5

2

2 つのCountDownLatchesと 1 つのSemaphoreを使用できます。最初のカウントダウン ラッチは、スレッドの開始を同期して、すべてのスレッドを同時に開始できるようにします。2 番目のカウントダウン ラッチは、スレッドの 1 つが終了したときに通知します。セマフォは、勝者のスレッドのみが完了することを許可し、どのスレッドが勝者であるかを尋ねている間に他のスレッドが終了する可能性がある競合状態を防ぎます。また、Bot クラスにある種の完了フラグを追加して、メイン スレッドがどちらが最初に完了したかを判断できるようにする必要がありますisAlive()

同時に開始するスレッドは、スレッドスケジューラに依存することに注意してください。サンプルコードは次のとおりです。

スレッドを作成して開始するスレッドコントローラー

  public void threadController() throws Exception
 {
    int numWorkers = 20;

    List<Worker> workerList = new ArrayList<Worker>(numWorkers);
    CountDownLatch startSignal = new CountDownLatch(1);
    CountDownLatch doneSignal = new CountDownLatch(1);
    //Semaphore prevents only one thread from completing
    //before they are counted
    Semaphore pauseForCheck = new Semaphore(1);

    for(int i=0; i<numWorkers; i++)
    {
       Worker worker = new Worker(i, startSignal, doneSignal, pauseForCheck);
       Thread thread = new Thread(worker);
       //worker has started, but will block on await();
       thread.start();
       workerList.add(worker);
    }

    //tell workers they can start
    startSignal.countDown();

    //wait for one thread to complete.
    doneSignal.await();

    //Look at all workers and find which one is done
    for (int i=0; i< numWorkers; i++)
    {
       if(workerList.get(i).isCompleted())
       {
          System.out.printf("Thread %d finished first\n", i);
       }
    }

    //add permits to semaphore so all losing threads can finish
    pauseForCheck.release(numWorkers - 1);
 }

実際に仕事をするワーカークラス

class Worker implements Runnable
{

   private final CountDownLatch startSignal;
   private final CountDownLatch doneSignal;
   private final Semaphore pauseForCheck;
   private final int id;
   private boolean completed = false;

   public Worker(int id, CountDownLatch startSignal, CountDownLatch doneSignal, Semaphore pauseForCheck )
   {
      this.id = id;
      this.startSignal = startSignal;
      this.doneSignal = doneSignal;
      this.pauseForCheck = pauseForCheck;
   }


   public boolean isCompleted()
   {
      return completed;
   }


   public void run()
   {
      try
      {
         //block until controller counts down the latch
         startSignal.await();
         //simulate real work
         Thread.sleep((long) (Math.random() * 1000));

         //try to get the semaphore. Since there is only
         //one permit, the first worker to finish gets it,
         //and the rest will block.
         pauseForCheck.acquire();

      }
      catch (InterruptedException e)
      {
         //don't care about this
      }

      //Use a completed flag instead of Thread.isAlive because
      //even though countDown is the last thing in the run method,
      //the run method may not have before the time the 
      //controlling thread can check isAlive status
      completed = true;

      //tell controller we are finished
      doneSignal.countDown();
   }
于 2013-10-20T17:11:28.460 に答える
0

私はこれをテストしませんでしたが、うまくいけば、コンパイルして、これを行う 1 つの方法についてのアイデアが得られることを願っています。

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;

final class Bot
  extends Thread
{

  private final CountDownLatch latch;

  private final AtomicReference<Bot> winner;

  Bot(String name, CountDownLatch latch, AtomicReference<Bot> winner)
  {
    super(name);
    this.latch = latch;
    this.winner = winner;
  }

  @Override
  public void run()
  {
    try {
      latch.await();
      winner.compareAndSet(null, this);
    }
    catch (InterruptedException ignore) {
      /* This thread was told to stop before completing its work. */
    }
  }

  private static final int BOT_COUNT = 3;

  public static void main(String[] argv)
    throws Exception
  {
    CountDownLatch latch = new CountDownLatch(1);
    AtomicReference<Bot> ref = new AtomicReference<>();
    Set<Bot> losers = new HashSet<>();
    for (int i = 0; i < BOT_COUNT; ++i) {
      Bot b = new Bot("Bot" + i, latch, ref);
      losers.add(b);
      b.start();
    }
    latch.countDown();
    for (Bot b : losers)
      b.join();
    Bot winner = ref.get();
    if (winner == null)
      System.out.println("No winner.");
    else {
      losers.remove(winner);
      System.out.println("Winner: " + winner.getName());
      for (Bot loser : losers)
        System.out.println("  Loser: " + loser.getName());
    }
  }

}

スレッドの開始を制御し、「勝つ」のは 1 つだけであることを保証する別のオプションは、BlockingQueue. ただし、その方向に進むとExecutorService、キャッシュされたスレッドで を使用する方がより良いアプローチであることがさらに強調されます。

于 2013-10-20T18:28:56.077 に答える
0

最速のスレッドの結果を使用する方法に関する自分の質問への回答を探しているときに、この質問を見つけました。しかし、私が受け取った( VGRによって書かれた)最良の答えは、この質問にも同様にうまく適用できるので(最初は別の解決策があると思っていました)、そうすることにしました。

IMO このタイプの問題は、ExecutorService.invokeAny()メソッドを使用するのに最適な場所です。本当に、すべてのスレッドをすぐに停止しようとしているわけではありません。むしろ、最速で完了したスレッドの結果を使用しようとしており、後続の計算を無視して、可能であれば停止していますが、それは二次的な問題です。次のコードは@lreeder's answerから取得したものですが、前述のinvokedAny()方法を使用して簡略化されています。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class InvokeAnyThreadController {
  public static void main(String[] args) throws Exception {
    new InvokeAnyThreadController().threadController();
  }

  public void threadController() throws Exception {
    int numWorkers = 20;

    List<Worker> workerList = new ArrayList<>(numWorkers);
    for (int i = 0; i < numWorkers; i++) {
      Worker worker = new Worker(i);
      workerList.add(worker);
    }

    ExecutorService execSvc = Executors.newFixedThreadPool(numWorkers);
    int firstInt = execSvc.invokeAny(workerList);
    System.out.println("firstInt=" + firstInt);
  }

  private static class Worker implements Callable<Integer> {

    private final int id;

    public Worker(int id) {
      this.id = id;
    }

    @Override
    public Integer call() {
      return this.id;
    }
  }
}

このコードは、 ( Runnableインターフェースではなく) Callableインターフェースを使用して、値を返すことができるようにします。への呼び出しは、他のすべてのスレッドを破棄して、高速計算の値を返すことが保証されています。その後、呼び出しはできるだけ早く他のすべてのスレッドを停止しますが、他のいくつかのスレッドも同様に計算を終了する可能性は十分にありますが、それらの結果は無視され、返されません。このメソッドを使用すると、下位レベルのスレッドや、 SemaphoresCountDownLatchesFutureTasksなどの他のクラスを使用する必要がなくなります。invokeAny()invokeAny()

于 2016-12-07T20:49:24.227 に答える
0

boolean isRunning各ボットと実行メソッドにwhile(isRunning). 次に、4 番目のスレッドで、すべてのボットを調べて、実行済みのボットがないかどうかを確認します。1 つが完了したらisRunning、他のボットの を に設定するfalseと、終了するはずです。

于 2013-10-20T17:04:48.927 に答える
0

すべての Bot クラスは同じコードを持っているので、Bot クラスを 1 つだけ作成し、bot1、bot2、bot3 の 3 つのオブジェクトを作成します。それらをスレッド コンストラクターに渡して、3 つのスレッドを作成します。

クラス変数を、ブール値のアクセス = false とします。また、読み取り/書き込みロックを許可する共有クラスも用意してください。Bot クラスの run メソッド内には、次のようなものがあります。

run(){
Lock();
if(accessed){
return;
}
syso("hello");
accessed = true;

Unlock();
}

最初に到達したスレッドはロックを取得し、アクセスされた変数を変更し、残りは変数が true に設定されていることを検出すると戻ります。

于 2013-10-20T17:05:17.710 に答える