2011-11-06 51 views
3

我有一個非常簡單直接的服務器(使用kryonet)。客戶只存儲汽車的當前狀態(x,y,角度等),併發送加速和轉彎請求。如何在不使用阻塞隊列的情況下將消息傳遞給另一個線程?

服務器正在接收請求並將它們添加到物理線程消耗並讀取和更新的ArrayBlockingQueue。

添加其他玩家時,遊戲速度減慢將近一倍。我已經排除了很多事情(我有所有的更新和封裝發送在60Hz扼殺)。

我懷疑使用阻塞隊列阻塞太多,導致放緩。

如何將客戶端請求發送到物理線程而不會阻塞問題?

+0

讓我直截了當地說:你希望每秒通過一個阻塞隊列傳遞60條消息,並認爲它是瓶頸? – meriton

+0

我不明白你的問題,我很新的阻止名單,所以請不要這樣做。 –

+0

如果隊列沒有填充,那麼你沒有阻塞。你的CPU運行在100%嗎?當你添加一個玩家時,你有多少玩家? –

回答

1

我發現了這個錯誤。我需要以一種不同的方式來扼制物理模擬(不是用world.step()函數,而是限制被調用的頻率)。這是這樣的。

while(true) 
{ 
    delta = System.nanoTime() - timer; 

    if(delta >= 16666666) // 60 Hz 
    { 
     world.step(1.0f, 6, 2); 

     processAndUpdateYourData(); 

     timer = System.nanoTime(); 
    } 
} 

然後,我需要調整所有的物理數字,讓他們感覺自然與這種配置。

0

你的問題預設了一個實現 - 你最好問「爲什麼我的代碼太慢?」。

阻塞隊列是實現生產者/消費者模式的最有效方式。

我會添加更多的消費者線程 - 嘗試添加儘可能多的消費者線程,因爲有處理器核心 - 即Runtime.getRuntime().availableProcessors()

+0

由於客戶端數據包的接收者是由kryonet庫實現的,因此我無法向服務器端添加更多線程。物理線程需要是1線程來模擬世界。 –

1

您可以使用干擾器(環形緩衝區),一個用於實現隊列的無鎖機制。請參閱:

+0

使用環形緩衝區(破壞者的心臟)可以消除併發鎖定帶來的極少量時間,但我無法想象時間會以每秒60個消息的速度減慢50%。 –

+0

+1爲酷紙。 :) –

0

確保您所創建的隊列可滿足所有的客戶足夠的位置。如果因爲客戶端太多而使隊列變滿,則嘗試插入命令時會阻塞隊列。如果這不是問題,這意味着你的物理(消費者)線程沒有跟上請求,你需要確保它獲得更多的處理時間。

+0

我可以看到隊列是否太滿。它會拋出一個異常並導致客戶端崩潰(當前)。而且這一刻不會發生。 –

2

我懷疑使用阻塞隊列阻塞太多,導致減速。

你懷疑是錯的。下面的測試程序,通過推動的ArrayBlockingQueue百萬整數:

public class ArrayBlockingQueuePerfTest { 
    int maxi = 1000000; 

    ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(1000, 
      true); 

    Thread sender = new Thread("sender") { 
     public void run() { 
      try { 
       for (int i = 0; i < maxi; i++) { 
        queue.offer(i, 1, TimeUnit.SECONDS); 
       } 
      } catch (InterruptedException e) { 
       throw new RuntimeException(e); 
      } 
     }; 
    }; 
    Thread receiver = new Thread("receiver") { 
     public void run() { 
      try { 
       int count = 0; 
       long sum = 0; 
       while (count < maxi) { 
        sum += queue.poll(1, TimeUnit.SECONDS); 
        count++; 
       } 
       System.out.println("done"); 
       System.out.println("expected sum: " + ((long) maxi) * (maxi - 1)/2); 
       System.out.println("actual sum: " + sum); 
      } catch (InterruptedException e) { 
       throw new RuntimeException(e); 
      } 
     }; 
    }; 

    public ArrayBlockingQueuePerfTest() { 
     sender.start(); 
     receiver.start(); 
    } 

    public static void main(String[] args) { 
     new ArrayBlockingQueuePerfTest(); 
    } 
} 

在我的筆記本電腦,它終止在幾秒鐘。因此,無論性能瓶頸在哪裏,都不是ArrayBlockingQueue,它可以處理比您需要的數據量高出至少3個數量級的吞吐量。換句話說,即使你找到了一個根本不需要執行時間的線程通信方法,那隻會使你的程序加速至多0.1%。

家庭作業,這和所有其他性能問題:解決在現有代碼的任何性能問題時,第一步是措施代碼的哪些部分是緩慢的,因爲通常情況下,它是不是其中一個希望。探查器極大地簡化了這項任務。

+0

我已經通過JVM分析器分析了代碼,我有大日誌,他們目前沒有向我展示任何東西。當添加第二名球員時,沒有差別,沒有秒殺。我現在懷疑問題不在於服務器,而在於客戶端。但非常感謝您的回覆。 –

相關問題