Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Managing threads in Grails Services

So I have a service set up to import a large amount of data from a file the user uploads. I want to the user to be able to continue working on the site while the file is being processed. I accomplished this by creating a thread.

Thread.start {
 //work done here
}

Now the problem arises that I do not want to have multiple threads running simultaneously. Here is what I tried:

class SomeService {

Thread thread = new Thread()

 def serviceMethod() {
   if (!thread?.isAlive()) {
     thread.start {
        //Do work here
     }
   }
 }

}  

However, this doesn't work. thread.isAlive() always return false. Any ideas on how I can accomplish this?

like image 544
James Kleeh Avatar asked May 30 '13 20:05

James Kleeh


2 Answers

I would consider using an Executor instead.

import java.util.concurrent.*
import javax.annotation.*

class SomeService {

ExecutorService executor = Executors.newSingleThreadExecutor()

 def serviceMethod() {
   executor.execute {
      //Do work here
   }
 }


 @PreDestroy
 void shutdown() {
   executor.shutdownNow()
 }

}

Using a newSingleThreadExecutor will ensure that tasks execute one after the other. If there's a background task already running then the next task will be queued up and will start when the running task has finished (serviceMethod itself will still return immediately).

You may wish to consider the executor plugin if your "do work here" involves GORM database access, as that plugin will set up the appropriate persistence context (e.g. Hibernate session) for your background tasks.

like image 63
Ian Roberts Avatar answered Oct 31 '22 12:10

Ian Roberts


Another way to do this is to use Spring's @Async annotation.

Add the following to resources.groovy:

beans = {
    xmlns task:"http://www.springframework.org/schema/task"
    task.'annotation-driven'('proxy-target-class':true, 'mode':'proxy')
}

Any service method you now annotate with @Async will run asynchronously, e.g.

@Async
def reallyLongRunningProcess() {
    //do some stuff that takes ages
}

If you only want one thread to run the import at a time, you could do something like this -

class MyService {
    boolean longProcessRunning = false

    @Async
    def reallyLongRunningProcess() {
        if (longProcessRunning) return

        try {
            longProcessRunning = true
            //do some stuff that takes ages
        } finally {
            longProcessRunning = false
        }
    }
}
like image 40
rcgeorge23 Avatar answered Oct 31 '22 14:10

rcgeorge23