2016-06-17 20 views
0

我想写一个遗传程序通过游戏玩,但我遇到了一点障碍。当我把这个代码:等待方法完成,并与System.println怪异的交互

public double playMap (GameBoard gb, Player p) { 
    gb.playerController = p; 
    Game g = new Game(gb); 
    int initHP = 0; 
    for (Unit u : gb.enemy.units) { 
     initHP += u.maxHP; 
    } 

    g.playGame(false); 

    int finalHP = 0; 
    for (Unit u : gb.enemy.units) { 
     finalHP += u.currHP; 
    } 
    System.out.println("  " + initHP); 
    System.out.println("  " + finalHP); 
    System.out.println("  " + (finalHP - initHP)); 
    if (initHP == finalHP) { 
     return -10; 
    } 
    return initHP - finalHP; 
} 

的g.playGame()线没有足够的时间来完成,而我正在从功能上不正确的结果。我可以等待游戏结束了,游戏结束了,但是没有使用相同的while循环没有打印语句。我知道必须有更优雅的解决方案,而且我似乎无法实现我所见过的方法。另外如果有人知道为什么我需要在while循环中的打印语句来让它等待,那也会很棒。

在此先感谢。

ADDED玩笑:

public void playGame(boolean visual) { 
    Global.visual = visual; 
    if (Global.visual) { 
     JFrame application = new JFrame(); 
     application.setBackground(Color.DARK_GRAY); 
     application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     application.add(this);   


     application.setSize(500, 400);   // window is 500 pixels wide, 400 high 
     application.setVisible(true); 
    } 

    PlayerInput pi = new PlayerInput(); 
    this.addKeyListener(pi); 

    final Timer timer = new Timer(10/60, null); 
    ActionListener listener = new ActionListener() { 
     @Override 
     public void actionPerformed(ActionEvent e) { 
      pi.addPressed(); 
      if (update(pi)) { 
//     application.setVisible(false); 
//     application.dispose(); 
       System.out.println(gb.toString()); 
       isDone = true; 
       timer.stop(); 

      } 
      pi.reset(); 
     } 
    }; 
    timer.addActionListener(listener); 
    timer.start(); 


    while (!isDone) { 
     System.out.println(isDone); 
    } 
} 
+0

你能告诉我们如何游戏类和它的方法playGame()是否被实现? –

+0

我添加的方法和类是相当大的,我将只是链接的混帐回购协议。 [https://github.com/RyTaus/SURP/blob/master/DoH/Game.java](https://github.com/RyTaus/SURP/blob/master/DoH/Game.java) –

回答

0

显然,你是在一个单独的线程中运行你的游戏。假设线程调用foo,调用foo.join()将阻塞调用线程,直到foo完成执行。您可以简单地用foo.join()替换整个循环。 如果您从循环中删除System.out.println()调用,我相信编译器不会将循环包含在Java字节码中,相信它是多余的。

+0

我会怎样找到为游戏创建的线程的名称?另外,当我做一些像增加一个变量并在循环之后打印它(为了不使它变得多余),java仍然不会等待。 –

+0

*“如果你从你的循环中删除的System.out.println()调用,我相信,编译器根本不包含在Java字节码的循环,认为它是多余的。” * - 它不会做那。实际的问题可能是'g.isDone'的读写操作没有正确同步。 –

+0

如何正确同步读取和写入? @StephenC? –

2

首先,这是一个非常糟糕的做法。这种方法被称为“忙等待”,效率非常低。

问题很可能是读取和写入g.isDone未正确同步。因此,不能保证“等待”线程有史以来看到更新g.isDone将其设置为true。

有多种方法可确保更新可见。最简单的方法是将isDone声明为volatile。另一个是在原始锁中进行读写操作。

println()调用“修复”事情的原因是println在幕后进行一些同步,这导致偶然的缓存刷新(或其他),使您的更新可见。 (换句话说:你真的很幸运,但究竟如何你很幸运是很难牵制。)


一个更好的解决方案是使用另一种机制来协调两个线程。

  • 你可以使用Thread.join()因此,对于另外一个一个线程等待终止(完全!)。

  • 您可以使用LatchSemaphore或类似的来实现等待。

  • 您可以使用提供FutureExecutor,然后致电Future.get()等待以提供结果。

  • 你甚至可以使用Object.waitObject.notify ...虽然这是低级的,容易出错。

没有看到完整的上下文,很难判断哪种方法最合适。但他们都会比忙等待更好。


另一个答案这样说:

如果您从您的循环中的System.out.println()调用,我相信,编译器根本不包含在Java字节码的循环,相信它是多余的。

正如我上面所解释的,真正的问题是同步不充分的。要技术,需要有一个之前发生的isDone在一个线程写入和isDone在另一个读之间关系。各种各样的事情会给那个......但没有说,编译器有权假设:

  1. 写入线程不需要写入刷新到内存
  2. 读线程并不需要检查内存已经改变。

例如,没有之前发生,编译器将被允许以优化

while (!g.isDone) { 
    // do nothing 
} 

if (!g.isDone) { 
    // do nothing 
} 

我们不知道这是否实际发生,或isDone更新的“不可见性”的实际原因是否是别的。 (事实上​​,它可能是特定JVM版本/平台。可以肯定的,你需要得到JIT编译器来转储方法的本机代码,该代码非常仔细地分析。)

+0

非常感谢。我知道这样做效率低下,但这是我唯一可以找出一个bandaid修复程序的方法。看起来像我有一些阅读要做的线程和同步。 –

+0

看起来你会。 –