簡體   English   中英

如何用Java編寫此等效代碼?

[英]How can I write this equivalent code in Java?

我有一個異步C ++函數,該函數需要將工作傳遞給另一個線程,然后等待該工作完成。 我已經使用std::promise對象完成了此操作,如下所示:

void task(std::function<void()> const& func) {
    std::promise<void> promise;
    //You can think of 'thread_pool' as being a wrapper around a std::vector<std::thread>
    //where all the threads have a body that more-or-less look like
    /* void run() {
     *     while(running) {
     *         task t;
     *         if(task_queue.try_pop(t)) t();
     *     }
     * }
     */
    thread_pool.post([&] {
        try {
            func();
            promise.set_value();
        } catch (...) {
            promise.set_exception(std::current_exception());
        }
    });

    promise.get_future().get();
}

所以我的問題是,用Java表達相同概念的最簡單方法是什么? 在我的特定情況下,我需要管理Swing線程和JavaFX線程之間的通信,並管理兩者之間的任務。 這是我到目前為止的內容:

public static void runAndWait(Runnable runner) {
    Future<Object> future = new FutureTask<>(new Callable<Object>() {
        public Object call() {
            try {
                runner.run();
            } catch (RuntimeException e) {
                //??? How do I report the exception to the future?
            }
            return null;
        }
    });

    Platform.runLater(/*How do I run the future I've just created?*/);

    future.get();//I want the exception to throw here if we caught one.
}

但是,顯然,我缺少一些東西。 如何表達用Java描述的C ++代碼?

您正在專注於錯誤的事情。 盡管Future可以支持您想要的行為-等待計算完成-這只是普通方法調用的語義。 Future是代表異步任務的完成,您可以在完成其他工作之后稍后對其進行檢查/檢索。 您不需要執行Future的整個合同。

您似乎在苦苦掙扎的主要事情是如何確定JavaFX線程何時完成了計算。 據我所知或可以確定的那樣,JavaFX對此沒有特定的接口。 它是圍繞JavaFX是應用程序的整體管理線程這一概念而設計的。 如果您想在該線程上進行工作並在完成時收到通知,則執行通知是工作的一部分。

例如,

public static void runAndWait(Runnable runner) {
    final SynchronousQueue<RuntimeException> exceptionQueue = new SynchronousQueue<>();

    Platform.runLater(
        // This Runnable wrapper performs synchronization with the invoking
        // thread via the SynchonousQueue
        new Runnable {
            public void run() {
                try {
                    runner.run();
                    exceptionQueue.put(null);
                } catch (RuntimeException re) {
                    exceptionQueue.put(re);
                }
            }
        });

    // blocks until an element is inserted into the queue:
    RuntimeException re = exceptionQueue.take();

    if (re != null) {
        throw new RuntimeException(re);
    }
}

您從錯誤的角度出發。 工作提交界面開始; 合適的Java接口是ExecutorService Java有幾種實現,包括線程池上的兩種變體和一種抽象實現,您應該能夠對其進行自定義以在您選擇的現有線程上運行任務。 您可能根本不需要實施Future 而是使用RunnableCallable表示工作單元,並讓ExecutorService提供合適的Future

另外,如果您願意稍微遠離C ++模型,並且不需要在特定線程上運行的工作,則可以考慮跳過ExecutorService並使用SwingWorker

這個問題似乎類似於:

您問題中的runAndWait代碼看起來非常類似於Sarcan的答案,這是:

final FutureTask query = new FutureTask(new Callable() {
    @Override
    public Object call() throws Exception {
        return queryPassword();
    }
});
Platform.runLater(query);
System.out.println(query.get());

在對其他答案的評論中,我注意到您也關注異常處理。 您會注意到,FutureTask具有setException()邏輯:

導致此將來以給定throwable作為其原因來報告ExecutionException,除非已經設置或取消了該將來。 計算失敗時, run()方法在內部調用此方法。

當內部實現調用setException調用時,您無需顯式調用setException 在FutureTask上下文中引發的任何未捕獲的異常都將在該FutureTask中設置,您可以通過捕獲來自future.get()調用中的ExecutionException來解釋該future.get()

// non-JavaFX thread code...

Future<Void> future = new FutureTask<>(() -> {
        // work to be done on the JavaFX thread...
        return null;
    }
});

// do the work on the JavaFX thread.
Platform.runLater(future);

try {
    // await completion of the work on the JavaFX thread.
    future.get();
} catch (InterruptedException ex) {
    // restore the interrupt status (see the linked Goetz article).
    Thread.currentThread().interrupt();
} catch (ExecutionException ex) {
    // exception handling logic for an exception occurring 
    // in the body of the FutureTask here.
}

在上面的示例中,我有一個Future<Void>因為我對傳遞Future調用的任何數據結果都不感興趣。 如果我有興趣獲得結果,則可以使用Future<SomeObjectType>並具有SomeObjectType result = future.get() 在這種情況下,我喜歡盡可能多地使用不可變的對象 (例如,對於SomeObjectType ),盡管對於future.get()場景並不是嚴格必須的,因為這實際上是按順序訪問對象,而不是跨線程並行訪問對象。

如果要重新引發在非JavaFX線程上的JavaFX應用程序線程上發生的異常,則可以這樣做:

} catch (ExecutionException ex) {
    throw ex.getCause();
}

以下信息用於與JavaFX(或Java)進行基於線程的不同交互,並且與問題沒有直接關系,因此對於回答問題的細節可以忽略,僅用作背景信息。

一些背景信息: Brian Goetz的文章在Java中實現任務方面非常出色:

相反的交互:上面給出的示例與從另一個線程調用JavaFX應用程序線程上的任務並等待其完成有關。 如果遇到相反的情況,您想在另一個線程而不是JavaFX線程上調用Task,則可以使用JavaFX Task 在這種情況下,您不希望JavaFX線程等待非JavaFX任務的完成,因為您永遠不要暫停或掛起JavaFX線程(相反,Task調用必須同時執行,鏈接中介紹了如何執行此操作任務Javadoc)。

在相關(但不同)的問題中詳細介紹了在后台線程和JavaFX UI之間進行交互的機制:

這是一個可能的解決方案:

public static void runAndWait(Runnable runner) {
    Future<Object> future = new FutureTask<>(new Callable<Object>() {
        public Object call() {
            runner.run();
            return null;
        }
    });

    try {
        future.get(); // exception handling happens here.
    } catch (InterruptedException ex) {
        Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, ex);
    } catch (ExecutionException ex) {
        Logger.getLogger(MainApp.class.getName()).log(Level.SEVERE, null, ex);
    }
}

CallableRunnable是單獨的接口,因此無法將Callable對象提交給Platform.runLater() ,除非您將Callable重新包裝在另一個Runnable內部,但這是荒謬的。 調用future.get()將導致對Future進行評估(同時阻止執行,可能不是您想要的)。 如果您省略了Callable的try / catch,則必須在調用future.get()時進行處理。 但是,如果不需要返回結果(因為您返回null ),則可以直接將可運行對象立即傳遞:

public static void runAndWait(Runnable runner) {
    try{
        Platform.runLater(runner);
    } catch (Exception ex) {
        // handle exceptions; generic Exception used for brevity
    }
}

但是,此版本無法讓您明確決定何時要運行任務。 每當Platform.runLater()決定運行它時,它將執行:

將來在某些未指定的時間在JavaFX Application Thread上運行指定的Runnable。 可以從任何線程調用此方法,該方法會將Runnable張貼到事件隊列中,然后立即返回給調用者。 Runnable將按照它們的發布順序執行。 傳遞給runLater方法的runnable將在傳遞給后續對runLater的調用的任何Runnable之前執行。 如果在關閉JavaFX運行時之后調用此方法,則該調用將被忽略:Runnable將不會執行,並且不會引發任何異常。

憑直覺,我認為這意味着第一個Runnable幾乎立即被執行,而隨后的Runnable在每個先前的Runnable完成時都被執行,但是我不知道是第一手的。


評論后,這是另一種可能性:

public static void runAndWait(Runnable runner) throws Exception {
    ExecutorService exec = Executors.newCachedThreadPool();
    exec.submit(runner);
    exec.shutdown();
    exec.awaitTermination(3l, TimeUnit.SECONDS);
}

這將使您設置等待時間的超時,該時間可以短短或長(在此示例中為3秒)。 這將阻塞執行,直到Runnable線程完成為止,因此,如果它在FX線程中運行,則無需通知它已完成,那么您應該能夠繼續執行超出此范圍的操作。

暫無
暫無

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

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