在Grails服务中管理线程

11

我已经设置了一个服务,可以从用户上传的文件中导入大量数据。我希望用户能够在文件处理过程中继续使用网站。我通过创建线程来实现这一点。

Thread.start {
 //work done here
}

现在问题出现了,我不想同时运行多个线程。这是我尝试过的方法:

class SomeService {

Thread thread = new Thread()

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

}  

然而,这并不起作用。thread.isAlive() 总是返回false。你们有什么想法可以让我实现这个吗?

2个回答

17

我会考虑使用一个 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 数据库访问,则可以考虑使用executor 插件 ,因为该插件将为您的后台任务设置适当的持久性上下文(例如 Hibernate 会话)。


它确实涉及到GORM。我真的不想仅为此安装插件。我能否避免使用它? - James Kleeh
@JamesKleeh 使用 withTransaction 应该可以解决这个问题(至少对于使用 hibernate 的情况,我不能确定 mongo),但是 executor 插件非常轻量级,所以不应该轻易忽略它。 - Ian Roberts
@JamesKleeh,你可以在源代码https://github.com/basejump/grails-executor/tree/master/src/groovy/grails/plugin/executor中获取所需的相关部分。如果我是你,我会直接使用插件,以避免处理潜在的持久性问题... - rimero
谢谢 - 当我使用线程时,我实际上已经有它了(呆)。 - James Kleeh
插件文档说明它只能在JDK6上运行。我在JDK7中使用了你提供的代码,没有任何问题。插件文档是旧的还是正确的? - James Kleeh
1
@JamesKleeh,我之前曾详细查看过插件代码,我看不出为什么它不能在7上运行。我理解"only works on 1.6"的意思是“不支持1.5或更早版本”。 - Ian Roberts

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

我感谢您的回答,但是我本来可以用标志设置变量来完成我想做的事情。只是我不太喜欢那种方式。 - James Kleeh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接