簡體   English   中英

使用ExecutorService確定將任務分配給線程

[英]Deterministic assignment of tasks to threads using ExecutorService

給定具有固定線程池的Executor服務,是否可以保證線程的確定性任務分配? 更確切地說,假設只有兩個線程,即pool-thread-0和pool-thread-1,並且有一組要執行的2個任務。 我希望實現的是前一個線程總是執行第一個,而后者處理剩下的一個。

這是一個例子:

public static void main(String[] args) throws InterruptedException, ExecutionException {
    ExecutorService executorService = newFixedThreadPool(2,
            new ThreadFactoryBuilder().setNameFormat("pool-thread-%d").build());

    for (int i = 0; i < 5; i++) {
        List<Callable<Integer>> callables = ImmutableList.of(createCallable(1), createCallable(2));
        executorService.invokeAll(callables);

    }
}

public static Callable<Integer> createCallable(final int task) {
    return new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            currentThread().sleep(1000);
            System.out.println(Thread.currentThread().getName() + " executes task num: " + task);
            return task;
        }
    };
}

我機器的示例輸出:

pool-thread-0 executes task num: 1
pool-thread-1 executes task num: 2

pool-thread-0 executes task num: 2
pool-thread-1 executes task num: 1

pool-thread-0 executes task num: 2
pool-thread-1 executes task num: 1

pool-thread-0 executes task num: 2
pool-thread-1 executes task num: 1

pool-thread-0 executes task num: 1
pool-thread-1 executes task num: 2

簡而言之,我希望確保pool-thread-0始終執行第一個任務。 任何幫助將不勝感激!

ExecutorService的目的不是為其Callable / Runnable提供“線程關聯”。 有人可能會說“這就是重點”,API可以讓程序員處理工作描述(Callable),而不是處理Thread處理。

您的設計,“有一個與每個線程關聯的隨機數據生成器”的地方不適合ExecutorService,原因有三個:

  1. 你無法控制將創建(或銷毀!)的線程以及什么時候(如果一個崩潰會怎樣?池會重新創建它但它會得到什么隨機生成器?)。 所以我們無法推斷一種可靠的方式來說“這個線程”有“這個生成器”,更不用說“第二個”線程有“這個生成器”,因為甚至可能沒有第二個線程(如果每個任務都如此之快,它們的處理速度比你發送的要快嗎?

  2. 您無法控制何時執行哪些任務。 好吧......使用Executors.newFixedThreadPool,你可以按照提交的順序調度它們,但是據你所知,OS調度程序可能會優先考慮線程1,最終會完成所有的工作,並且線程2根本不做任何事情(它可以是任何比例)。

  3. 將“數據生成器”傳遞給線程的唯一方法是覆蓋執行程序服務的ThreadFactory。 否則,您無法訪問線程實例(運行時來自可調用的appart)。 因此,要將特定生成器關聯到特定線程,您必須知道當前正在創建哪個線程編號,如果您計算線程,這很容易,但如果您想知道什么是Callable,則很難線程旨在(見第2點)。

因此,我強烈建議您定義一些將工作單元與數據生成器關聯的其他方法,因為“線程實例”通常不可靠 - 至少不通過Executor Service。 例如,當你說

我需要提供它們處理的線程和數據的組合是可重復的。

我知道您將始終發送一定數量的Callables,並且您需要每個Callables處理由特定生成器發出的特定數據集。 假設我們有一定數量的任務和3個生成器,任務(N)將使用生成器N%3

為了使結果可重復,您還需要使用相同生成器的任務不會同時執行(您希望通過線程關聯實現什么?)。

有一定數量的模式可以實現這一點。

1是:對生產者/消費者的重構(反過來做)

在您的執行程序服務中創建3個任務,每個任務偵聽一個BlockingQueue (其私有等待列表)並擁有自己的私有生成器。 這些是消費者。
使您的主線程成為生產者:當它創建工作單位(原始設計中曾經是可Callable )數字N時,將其分配給等待隊列號N%3。 就是這樣:每個消費者將按順序依次接收自己的數據,按照您希望的順序進行計算。 你已經達到了“親和力”。

2是:自己完成任務調度任務。 (這是hacky方式)

首先,重構您的callables以獲得他們需要使用的生成器的鏈接。 然后,在主線程上,構建要為每個生成器運行的任務列表。
從主線程調度每個生成器的第一個任務。
並且每個callable的結束,使Callable從其列表中調度下一個工作單元。
但是,如果你從callables中調用callables,請不要等待結果,因為這會阻止Callables完成,這反過來會阻止新調度執行。 這是一個僵局。

3:與2相同,效率較低,但風險較小

而不是從callables內部調度Callables,只通過等待期貨從你的主線程發送。

執行這兩種方法中的任何一種,您無法保證哪些任務將首先完成或最后完成,但您可以保證您發送的工作單元可預測地與您控制的數據生成器相關聯,並且它們將按您發送的順序執行他們。 希望這足夠了。

暫無
暫無

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

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