3

我对 Groovy 线程有疑问。

我的任务是以某种方式翻译给定目录中的每个文件,并将结果输出放在另一个目录中的文件中。

我编写了以下代码,该代码有效:

static def translateDir(fromDir, targetDir) {
    def allFiles = new File(fromDir).listFiles()
    def numFiles = allFiles.length

    for (i in 0..(numFiles - 1))
        translate(allFiles[i].getAbsolutePath(), targetDir)
}

现在,我尝试像这样并行化这段代码:

static def translateDir(fromDir, targetDir) {
    def allFiles = new File(fromDir).listFiles()
    def numFiles = allFiles.length
    def numCores = Runtime.getRuntime().availableProcessors()

    for (i in 0..(numCores - 1)) {
        println("Thread " + i + "starting")
        Thread.start {
            for (def j = i; j < numFiles; j += numCores) {
                println("j = " + j) 
                translate(allFiles[j].getAbsolutePath(), targetDir)
            }
        }
    }
}

这不起作用并提供输出:

Thread 0 starting
Thread 1 starting
Thread 2 starting
Thread 3 starting

在我的测试用例中,nunCores 是 4,numFiles 是 3。这里发生了什么?

4

2 回答 2

11

您正在启动线程,但是您的程序在它们运行之前完成。

文档中所示,您需要调用join线程以等待它们完成

使用执行器框架可能更容易,该框架在同一页面的下方显示


编辑

只是为了好玩,我使用Thread.start它实现了一些你想要实现的东西

// This will perform the task required for a list of files
def translate( List files ) {
  files.each {
    // We will just print it out, and sleep for a bit
    println it
    Thread.sleep( 300 )
  }
}

// makeWorker returns a closure that will process the files
def makeWorker( List files ) {
  { -> translate( files ) }
}

// Main processing function
void process( List files ) {
  int numCores = Runtime.getRuntime().availableProcessors()
  // Split the files to process into groups (one for each worker)
  def partitioned = (0..<numCores).collect { i ->
    files[ (i..<files.size()).step( numCores ) ]
  }
  // Then create a Thread for each group
  def threads = partitioned.collect { group ->
    Thread.start makeWorker( group )
  }
  // And wait for them all to finish
  threads*.join()
}

// Simulate the process with the letters a through z
process( 'a'..'z' )
于 2012-05-28T22:13:47.630 回答
8

好的,有两件事:

  1. 在线程的隐式 run() 方法中,您正在引用“i”变量。我必须通过调试器逐步了解会发生什么,但从技术上讲,你甚至不应该在那里访问“i”,因为它不是最终的。因此,我建议创建一个 Runnable 对象,在构造函数中将“i”传递给该对象。然后在线程上启动runnable。

  2. Groovy 有很好的并发支持——没有理由像这样滚动你自己的。看看GPar

于 2012-05-28T22:12:14.530 回答