如何从外部进程捕获输出线程安全?

时间:2016-12-02 16:06:48

标签: multithreading groovy process gpars

我编写了一个小方法来执行git命令行工具并捕获其输出:

def git(String command) {
    command = "git ${command}"

    def outputStream = new StringBuilder()
    def errorStream = new StringBuilder()
    def process = command.execute()
    process.waitForProcessOutput(outputStream, errorStream)

    return [process.exitValue(), outputStream, errorStream, command]
}

我将它与GPars一起使用来同时克隆多个存储库,如

GParsPool.withPool(10) {
    repos.eachParallel { cloneUrl, cloneDir->
        (exit, out, err, cmd) = git("clone ${cloneUrl} ${cloneDir}")
        if (exit != 0) {
            println "Error: ${cmd} failed with '${errorStream}'."
        }
    }
}

但是,我相信我的git方法不是线程安全的:例如,第二个线程可以在第一个线程到达command之前修改方法第一行中的command.execute()在方法的第五行。

我可以通过制作整个git方法synchronized来解决这个问题,但这会破坏在不同线程中运行它的目的,因为我希望克隆并行发生。

所以我想像

那样进行部分同步
def git(String command) {
    def outputStream
    def errorStream
    def process

    synchronized {
        command = "git ${command}"

        outputStream = new StringBuilder()
        errorStream = new StringBuilder()
        process = command.execute()
    }

    process.waitForProcessOutput(outputStream, errorStream)

    return [process.exitValue(), outputStream, errorStream, command]
}

但我想这也不安全,因为第二个waitForProcessOutput()线程可能比第一个线程更早返回,搞砸了outputStream / errorStream个变量。

获得此线程安全的正确方法是什么?

1 个答案:

答案 0 :(得分:1)

更改eachParallel闭包参数中的赋值语句,如下所示:

        def (exit, out, err, cmd) = git("clone ${cloneUrl} ${cloneDir}")

这将使变量的局部变量成为局部变量,从而使它们成为线程安全的。 git()方法原样很好。