繁体   English   中英

Grails中的多线程-将域对象传递到每个线程中会导致某些字段随机为null

[英]Multithreading in Grails - Passing domain objects into each thread causes some fields to randomly be null

我试图通过引入并行编程来加快Grails应用程序中的过程。 此特定过程需要筛选成千上万的文档,从文档中收集必要的数据并将其导出到excel文件。

经过数小时的尝试以追踪该过程为何进行得如此缓慢的原因,我确定该过程必须完成大量工作,以从每个域对象收集数据的特定部分。 (示例:域对象内部有数据列表,此过程将这些列表中的每个索引都添加到带有逗号的字符串后,从而在excel表格的单元格中形成漂亮的排序列表。还有更多示例但这些不重要。)

因此,任何不简单的数据访问(document.id,document.name等)都会导致此过程花费很长时间。

我的想法是对每个文档使用线程来异步获取所有这些数据,当每个线程完成对数据的收集后,它可以回到主线程并放置到excel工作表中,现在所有这些都可以通过简单的数据访问,因为该线程已经收集了所有数据。

这似乎在工作,但是我在域对象和线程上有一个错误。 每个线程都在其相应的文档域对象中传递,但是无论出于何种原因,文档域对象都会将其数据的某些部分随机更改为null。

例如:在将文档传递到线程之前,域对象的一部分将具有一个如下所示的列表:[美国,英格兰,威尔士],在任意位置,该列表在线程中的外观都将如下所示: [美国,null,威尔士]。 这会在任何随机时间在域对象的任何随机部分发生。

生成线程:

def docThreadPool = Executors.newFixedThreadPool(1)
def docThreadsResults = new Future<Map>[filteredDocs.size()]
filteredDocs.each {
    def final document = it
    def future = docThreadPool.submit(new DocumentExportCallable(document))
    docThreadsResults[docCount] = future
    docCount++
}

从线程取回数据:

filteredDocs.each {
        def data = docThreadsResults[count].get()

        build excel spreadsheet...
}

DocumentExportCallable类:

class DocumentExportCallable implements Callable {
    def final document

    DocumentExportCallable(document) {
        this.document = document
    }

    Map call() {
            def data = [:]

            code to get all the data...

            return data
    }
}

编辑:如下所示,如果我可以向您显示域对象,这将很有用。 但是,我无法做到这一点。 但是,你们问我关于领域对象的事实让我认为这可能就是问题所在。 事实证明,域对象的随机散布在线程中的每个部分都是“映射”内部域对象中的一个变量,该变量使用SQL连接获取这些变量的数据。 我刚刚意识到Grails中的懒惰与渴望获取。 我想知道这是否可能是问题所在...默认情况下,它设置为延迟获取,因此每个线程对数据库的这种恒定访问可能是问题所在。 我相信找到一种将其更改为渴望获取的方法可能会解决此问题。

我对为什么这些空值随机出现的答案。 现在一切似乎都可以正常运行,并且我的实现现在比以前的实现快得多!

原来,我不知道具有1-m关系的Grails域对象在访问这些字段时也会进行单独的sql调用,即使在获取对象本身之后也是如此。 这一定已导致这些线程进行非线程安全的sql调用,这些调用创建了这些随机null值。 在这种特定情况下,将这些1-m属性设置为容易获取时,可以解决此问题。

对于以后阅读的任何人,您都想读一遍懒惰与渴望获取的内容,以获得更好的理解。

至于代码:

这些是我域对象中存在的1-m变量:

static hasMany = [propertyOne : OtherDomainObject, propertyTwo : OtherDomainObject, propertyThree : OtherDomainObject]

我在数据库调用中添加了一个标志,用于在特定情况下启用此代码,因为我不希望始终在整个应用程序中始终获取这些属性:

if (isEager) {
    fetchMode 'propertyOne', FetchMode.JOIN
    fetchMode 'propertyTwo', FetchMode.JOIN
    fetchMode 'propertyThree', FetchMode.JOIN
    setResultTransformer Criteria.DISTINCT_ROOT_ENTITY
}

不好意思,但是此刻我不记得为什么我必须在上面的代码中放入“ setResultTransformer”,但是如果没有它,就会出现问题。 也许以后有人可以解释这个,否则我确定Google搜索会解释。

发生的事情是,您的grails域对象正在从休眠会话中分离,因此当您的线程尝试加载惰性属性时,出现LazyInitiationException异常。

切换到热切获取对您有用,但这并不是每个人的选择。 您可能还可以使用grails异步任务框架,因为它具有内置的会话处理功能。 参见https://async.grails.org/latest/guide/index.html

但是,即使使用grails异步任务,在线程之间传递对象也似乎将其分离,因为新线程将具有新绑定的会话。 我已经找到了在新线程.attach().merge()绑定到调用线程上的会话的解决方案。

我相信最佳的解决方案是让休眠对象在新线程上加载,这意味着在代码段中,您将在会话支持的线程上传递文档id和Document.get(id)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM