除非我包含 println 或断点,否则代码不起作用



我正在制作一个基本的多人射击游戏,作为大学计算机图形学课程的一部分。

对于客户端>服务器通信,我使用的是UDP。每个客户端都有一个客户端控制器线程,该线程处理用户输入并通过套接字将其发送到服务器。服务器正在运行 ServerClientManager,该管理器处理消息,并在游戏状态中更新玩家的对象。

下面是客户端控制器的代码:

/**
 * A class that handles communication from client to server. 
 */
public class ClientController implements Runnable {
    private String serverAddress;
    int serverPort;
    Integer clientId;
    @SuppressWarnings("unused")
    private boolean forward, backward, left, right, fire, up, down; 
    public ClientController (String serverAddress, int serverPort) {
        System.out.println("ClientController started");
        this.serverAddress = serverAddress; 
        this.serverPort = serverPort; 
        clientId = null; 
        reset(); 
    }
    public synchronized void forward() {
        forward = true; 
    }
    ...
    private synchronized void reset() {
        forward = false; 
        backward = false; 
        left = false; 
        right = false; 
        fire = false; 
        up = false; 
        down = false; 
    }
    @Override
    public void run() { 
        System.out.println("ClientController thread is running");
        while (true) {
            //System.out.println("tick");
            if (clientId != null) {         
                try {
                    ClientUpdate update = new ClientUpdate(forward, backward, left, right, fire, up, down, clientId); 
                    ClientUDPSender sender = new ClientUDPSender(serverAddress, 1233, update.toJson());
                    Thread worker = new Thread(sender); 
                    worker.start(); 
                    Thread.sleep(15);
                    reset(); 
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public synchronized void setClientId(int clientId) {
        this.clientId = clientId; 
    }
}

这会向服务器发送更新。启动时,客户端启动此线程。然后,客户端通过另一个类中的TCP单独接收其clientId(初始游戏状态和连接信息通过TCP发送)。设置客户端 ID 后,UDP 客户端开始通信。

奇怪的是,除非我做以下两件事之一,否则这一切都不起作用:

1) 取消注释 System.out.println("tick") 行。

2) 在 try 语句中放置一个断点,然后从那里继续。

如果我只是按原样运行程序,它永远不会到达 try 语句的内部。如果我包含 println 或断点,它会运行良好,并且更新会按预期传播到服务器。

我很困惑。

奇怪的是,除非我做以下两件事之一,否则这一切都不起作用:

1) 取消注释 System.out.println("tick") 行。

PrintStream.println(...)是一种synchronized方法,所以我怀疑您的问题与内存同步有关。

然后,客户端通过另一个类中的TCP单独接收它的clientId

如果我理解的话,似乎clientId是由另一个线程设置的。 如果这是真的,则必须将其标记为volatile否则旋转线程将看不到更新。 即使setClient(...)方法synchronized while(true)循环中也没有任何东西可以跨越内存屏障并更新旋转线程内存缓存中的clientId值。 将clientId标记为volatile后,您可以删除 setter 上的 synchronized 关键字。

volatile Integer clientId;

此外,旋转非常恶心。 也许放一个Thread.sleep(10)什么东西来减慢它的速度?

相关内容

最新更新