Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invoke function on main thread from background thread

Update

I've updated this question to include source code of a Java implementation, using the suggested SwingWorker class, to accomplish the same result as the Objective-C example. Hopefully this will be of aid to future adventurers.

Document myDoc = ...;
Model myModel = ...;

SwingWorker analyzeDocument = new SwingWorker<Dictionary, Void>() {
    @Override
    public Dictionary doInBackground() {
        return myDoc.analyze();
    }

    @Override
    public void done() {
        try {
            stats = get();
            myModel.setDict(stats);
            myModel.setNeedsDisplay(true);
        } catch(InterruptedException ex) {
            // log
        } catch(ExecutionException ex) {
            // log
        }
    }
};

analyzeDocument.execute();

Original Post

I'm inexperienced when it comes to concurrent programming and I was hoping someone may be able to explain to me how I can achieve this.

In Objective-C (with GCD), you're able to do something like this:

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
     NSDictionary *stats = [myDoc analyze];
     dispatch_async(dispatch_get_main_queue(), ^{
       [myModel setDict:stats];
       [myStatsView setNeedsDisplay:YES];
       [stats release];
     });
 });

This code will execute [myDoc analyze] in the background thread, following a callback function to update the UI which will executed in the main thread. In other words, the background thread sends an interrupt to the main thread, adding the anonymous function to the main thread's queue to be invoked. Obviously I won't be able to use anonymous functions in Java, but that's beside the point.

I am interested in doing something to this effect in Java. I have a Runnable object which does a bunch of stuff in the file system. When it finishes, I want to update the UI accordingly.

In an effort to not hang the main thread while doing this (ie: backgroundThread.join();), I've set the background thread to execute a callback function to update the UI. But this isn't a good idea, I don't want a non-GUI thread updating the GUI.

Some other ideas I've thought of is polling, but that seems like throwing cycles out the window. By the same judgement, using Futures doesn't seem to be the answer either. This all seems to defeat the purpose of an asynchronous action.

The only other thing I can think of is using SwingUtilities.invokeLater from the background thread and using it to update the GUI. But I'm curious in which thread this would be executed.

Maybe my perception is just warped, but this seems like it would be a pretty big part to leave out. Am I just trying to go about this the wrong way?

like image 212
Ian Bishop Avatar asked Feb 24 '23 07:02

Ian Bishop


2 Answers

Have you looked into creating and using a SwingWorker object? This allows you to easily run code in a thread background to the event thread, and in fact is a Future object as well and so it can return a result, and you can use this result in its done() method to update the GUI on the event thread. You can even do calls on the Swing event thread in the middle of its run via the publish/process method pair.

For more on this, please check out: Concurrency in Swing

like image 103
Hovercraft Full Of Eels Avatar answered Mar 04 '23 04:03

Hovercraft Full Of Eels


If you use Callable<V> instead of Runnable, you can return a value.

Also, take a look at class Executors.

like image 30
Luigi Plinge Avatar answered Mar 04 '23 05:03

Luigi Plinge