英文:
GroovyShell Thread safety
问题
问题出现在所有关于GroovyShell的问题的注释中,比如https://stackoverflow.com/questions/5323114/using-groovyshell-as-expression-evaluator-engine-or-how-to-reuse-groovyshell。这并不令人意外,因为API设计似乎根本没有涵盖这个主题。不幸的是,这从未被明确讨论过。
问题的简洁表述如下:
静态初始化:
final GroovyShell shell = new GroovyShell();
final Script thisScript = shell.parse("sleep 1000; return input1+' '+input2");
//anotherScript = // 这里与问题无关,但我的用例预加载了大约300个Groovy脚本
脚本运行器:
private Object runScript(Script theScript, String param1, String param2) {
theScript.setProperty("input1", param1);
theScript.setProperty("input2", param2);
Object result = theScript.run();
return result;
}
序列化执行:
runScript(thisScript, "Hello", "World") -> Hello World
runScript(thisScript, "Guten", "Tag") -> Guten Tag
并行执行:
runScript(thisScript, "Hello", "World") -> Guten Tag (!)
runScript(thisScript, "Guten", "Tag") -> Guten Tag
问题在于绑定(无论是get/setBinding还是setProperty)是在脚本级别上进行的。这就像在通过类加载器加载java.lang.Class对象后在其上设置内容,或者修改静态成员变量一样。是否有另一种替代的Groovy实现可以将绑定和运行作为一个原子操作处理?或者更好的是:是否使用上下文对象进行执行?
最简单的解决方法是将runScript()
同步到脚本对象,但这不具有可扩展性。
英文:
The question appears in comments of all questions about GroovyShell, like https://stackoverflow.com/questions/5323114/using-groovyshell-as-expression-evaluator-engine-or-how-to-reuse-groovyshell. Not a surprise, since the design of the API seems it's not covering this topic at all. Unfortunately, this was never discussed explicitly.
The problem in a compact form:
Static initialization:
final GroovyShell shell = new GroovyShell();
final Script thisScript = shell.parse("sleep 1000; return input1+' '+input2");
//anotherScript = // not relevant here but my use-case pre-loads ~300 groovy scripts
script runner:
private Object runScript(Script theScript, String param1, String param2) {
theScript.setProperty("input1", param1);
theScript.setProperty("input2", param2);
Object result = theScript.run();
return result;
}
serialized execution:
runScript(thisScript, "Hello", "World") -> Hello World
runScript(thisScript, "Guten", "Tag") -> Guten Tag
parallel execution:
runScript(thisScript, "Hello", "World") -> Guten Tag (!)
runScript(thisScript, "Guten", "Tag") -> Guten Tag
The problem is that the binding (no matter if get/setBinding or setProperty) is done on script level. That's like setting something on a loaded java.lang.Class object after loading it through the classLoader or modifying static member variables. Is there an alternative groovy implementation which handles binding and running as an atomic operation? Or even better: uses a context object for execution?
The simplest workaround is synchronizing runScript()
to the script object but this doesn't scale.
答案1
得分: 5
// 创建不同的脚本类实例以便并行运行
GroovyShell shell = new GroovyShell();
Class<Script> scriptClass = shell.parse("sleep 1000; return input1+' '+input2").getClass();
Object runScript(Class<Script> clazz, String param1, String param2) {
Script theScript = clazz.newInstance();
theScript.setProperty("input1", param1);
theScript.setProperty("input2", param2);
Object result = theScript.run();
return result;
}
// 线程测试
[
["111", "aaa"],
["222", "bbb"]
].collect { x ->
Thread.start {
println "start $x"
println runScript(scriptClass, x[0], x[1])
}
}*.join()
输出:
start [111, aaa]
start [222, bbb]
111 aaa
222 bbb
英文:
create different instances of script class to run them in parallel.
GroovyShell shell = new GroovyShell();
Class<Script> scriptClass = shell.parse("sleep 1000; return input1+' '+input2").getClass();
Object runScript(Class<Script> clazz, String param1, String param2) {
Script theScript = clazz.newInstance();
theScript.setProperty("input1", param1);
theScript.setProperty("input2", param2);
Object result = theScript.run();
return result;
}
//thread test
[
["111","aaa"],
["222","bbb"]
].collect{x->
Thread.start{
println "start $x"
println runScript(scriptClass, x[0], x[1])
}
}*.join()
output:
start [111, aaa]
start [222, bbb]
111 aaa
222 bbb
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论