Управление потоками в сервисах Grails

Итак, у меня есть служба, настроенная для импорта большого количества данных из файла, который загружает пользователь. Я хочу, чтобы пользователь мог продолжить работу над сайтом во время обработки файла. Я выполнил это, создав поток.

Thread.start {
 //work done here
}

Теперь возникает проблема, что я не хочу, чтобы одновременно выполнялось несколько потоков. Вот что я пробовал:

class SomeService {

Thread thread = new Thread()

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

}  

Однако это не работает. thread.isAlive() всегда возвращает false. Любые идеи о том, как я могу это сделать?

Ответ 1

Вместо этого я хотел бы использовать Executor.

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()
 }

}

Использование newSingleThreadExecutor гарантирует, что задачи выполняются один за другим. Если уже запущена фоновая задача, следующая задача будет поставлена ​​в очередь и начнется, когда закончится работающая задача (serviceMethod сама по-прежнему будет немедленно возвращаться).

Возможно, вы захотите рассмотреть плагин исполнителя, если ваш "работать здесь" включает доступ к базе данных GORM, поскольку этот плагин настроит соответствующий (например, сеанс Hibernate) для ваших фоновых задач.

Ответ 2

Другой способ сделать это - использовать аннотацию Spring @Async.

Добавьте в resources.groovy следующее:

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

Любой метод обслуживания, который вы теперь аннотируете с помощью @Async, будет выполняться асинхронно, например.

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

Если вам нужен только один поток для запуска импорта за раз, вы можете сделать что-то вроде этого -

class MyService {
    boolean longProcessRunning = false

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

        try {
            longProcessRunning = true
            //do some stuff that takes ages
        } finally {
            longProcessRunning = false
        }
    }
}