簡體   English   中英

如何在 ExecutorService 中運行順序任務

[英]How to run sequential Tasks in an ExecutorService

我想在一個事件執行器中為每個玩家執行不同的任務,但要確保為每個玩家提交的任務是有序的。 這是一張圖片,希望能更好地解釋。

我試圖完成的事情的視覺表示

額外的信息:

  1. 我知道每個任務會影響哪個玩家
  2. 我以偽隨機順序接收每個玩家的任務。 (我不會事先知道任務輸入的順序)
  3. 我需要每個玩家基本上都有自己的“線程”,以便他們的任務按插入順序執行。

我嘗試過的:

目前我在每個玩家對象中使用Executors.newSingleThreadExecutor()來模仿這個功能,但我認為這不會很好地適應很多玩家。 因此,為什么我在這里要求找到更好的方法。

我看到了實現這一目標的三種主要方法。

每個玩家的線程

這有點像你正在做的事情。 為每個玩家創建一個線程,將工作單元分派給它; 這本質上是連續的。

可能無法很好地擴展到數百/數千個玩家(數百個活動線程通常不會嚇到現代硬件 - 盡管它可能不是很有效)。

但是代碼的可讀性和簡潔性可能是其他解決方案無法比擬的。

具有一致路由的共享線程

如果每個玩家一個線程是不可能的,那么我們將不得不共享線程,一個線程將處理多個玩家。

關鍵是讓給定的線程處理給定玩家的每個任務,從而實現串行執行。

我想到了三種構建它的方法......

創建玩家組

如果你的球員有一些東西可以分組(例如一個團隊,甚至只是一個創建日期)......然后按組重新分組你的球員,並實施“每組線程”模式。

唯一的關鍵問題是擁有大致相同大小的組,以便平均分擔負載。

幾乎在每個解決方案中都會遇到的另一個問題是關閉組的線程(您怎么知道您想要處理的所有內容何時完成?)。

共享線程和 ExecutorService

這就是“自己動手”。 它的要點是

  1. 創建一個fixedThreadPool ,比如說有 4 個線程。
  2. 創建一個 4 BlockingQueue來保存每個工作單元
  3. 創建 4 個PlayerWorker實例並將它們發送到線程池執行,上面的每個BlockingQueue 這些工作者的實現是從隊列之一中提取數據並執行它。
  4. 通過將它們發送到隊列來調度您的播放器工作單元。 這里的關鍵是始終為同一個玩家重用同一個隊列(例如,如果你的玩家有一個標識符屬性,比如一個 long,然后將所有工作單元分派到Math.abs(id % 4) th 隊列
  5. 一切完成后關閉PlayerWorker和線程池

這是很多工作,所以......

使用框架

有些人已經為你做了。 演員模式非常適合這個問題。 您可能應該查看 Akka 和所謂的ConsistentHashingRoutingLogic ,它幾乎是一對一的映射,至少在概念上是我剛剛描述的。

反應式

如果我們退后一點,我們實際上並不關心線程 我們關心串行執行

因此,我們只需要一種在其他工作完成后執行某些工作的方法。 應該只是回電!

我們有CompletableFuture (更不用說框架)。

那么為什么不為每個玩家創建一個CompletableFuture ,並且每次要為該玩家完成一個新的工作單元時,請使用CompletableFuture.thenApplythenApplyAsync

通過將執行分派到適當大小的執行池,並讓框架完成工作,任務將一個接一個地串行執行。

您可以為每個玩家創建一個singleEventExecutorService

class PlayerEventHandler {
    private Map<Player, ExecutorService> executors = new ConcurrentHashMap<>();

    public void handleEvent(PlayerEvent e) {
        ExecutorService executor = executors.computeIfAbsent(e.getPlayer(), Executors.newSingleThreadExecutor());
        executor.submit(e.execute());
    }
}

盡管對於 600 名玩家來說,這可能比單線程執行器表現得更糟。 但是,您可以通過某種方式對玩家進行分組,以便合理數量的玩家共享一個執行者。

class PlayerEventHandler {
    private Map<PlayerGroup, ExecutorService> executors = new ConcurrentHashMap<>();

    public void handleEvent(PlayerEvent e) {
        PlayerGroup group = determineGroup(e.getPlayer());
        ExecutorService executor = executors.computeIfAbsent(group, Executors.newSingleThreadExecutor());
        executor.submit(e.execute());
    }
}

如果玩家在運行時動態變化,這可能會成為一個問題,因為這樣你最終可能會用一個線程處理很多玩家,而另一個組隨着時間的推移變得空了。

這真的取決於你的實際情況。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM