我正在开发一款小型游戏(Java,LibGdx),其中玩家使用预定义的代码行填充完形式函数。然后游戏将编译代码并运行一个小测试套件来验证函数是否完成了它应该的东西。
编译和运行代码已经有效,但我遇到了检测无限循环的问题。考虑以下功能:
// should compute the sum of [1 .. n]
public int foo(int n) {
int i = 0;
while (n > 0) {
i += n;
// this is the place where the player inserts one of many predefined lines of code
// the right one would be: n--;
// but the player could also insert something silly like: i++;
}
return i;
}
请注意,实际使用的功能可能更复杂,一般情况下无法确保不存在任何无限循环。
目前我正在使用ExecutorService
在线程中运行小测试套件(为每个函数提供),设置超时以在线程卡住时中止等待。这个问题是,陷入无限循环的线程将在后台永远运行,这当然会在某些时候对游戏性能产生相当大的影响。
// TestClass is the compiled class containing the function above and the corresponding test suite
Callable<Boolean> task = new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
// call the test suite
return new TestClass().test();
}
};
Future<Boolean> future = executorService.submit(task);
try {
Boolean result = future.get(100, TimeUnit.MILLISECONDS);
System.out.println("result: " + (result == null ? "null" : result.toString()));
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
future.cancel(true);
}
我现在的问题是:如何优雅地结束无意中在无限循环中旋转的线程?
*编辑为了澄清为什么在这种情况下,防止无限循环是不可能/不可行的:函数,它们的测试套件和填补空白的线是从磁盘加载的。将有可以插入至少两行代码的函数的hundrets。玩家可以将任何行拖到任何间隙中。确保没有函数间隙/代码行的组合产生无限循环甚至运行时间长于超时的函数所需的努力随着函数的数量呈指数增长。这很快就达到了没有人有时间手动检查所有这些组合的程度。此外,一般来说,由于停止问题,确定一个函数是否会及时完成几乎是不可能的。
答案 0 :(得分:1)
正确的方法是改变设计并避免永无止境的循环。
目前,在你的循环中你可以通过以下方式检查线程是否被中断: isInterrupted()甚至 isAlive()。
如果是你就退出。
答案 1 :(得分:1)
在同一个进程中没有“正常终止”线程的东西。终止的线程可能会在其后面留下不一致的共享内存状态。
您可以组织事物,以便每个任务都在自己的JVM中启动,或者使用不推荐使用的Thread.stop()
方法强制终止。
另一种选择是在生成的代码中插入一个检查,但这需要更多的努力才能正确实现。
答案 2 :(得分:0)
如果不想要一个永无止境的循环是不正常的。
解决问题您可以在循环中添加计数器,如果达到限制,则可以退出。
int counter = 0;
while (n > 0) {
counter++;
if (counter > THRESHOLD) {
break;
}
i += n;
// this is the place where the player inserts one of many predefined lines of code
// the right one would be: n--;
// but the player could also insert something silly like: i++;
}