4.3. Multithreading in Scripts

Sometimes it can be convenient to have work done in parallel. The common way to do this in Groovy or Java is to conjure up multiple threads that do the actual work and run them in parallel. Usually you would want to have the threads controlled by a thread pool. For this ReportServer provides an easy mechanism. The following script is a simple example of how to create thread pools using ReportServer's DwAsyncService (net.datenwerke.async.DwAsyncService):

import net.datenwerke.async.DwAsyncService
import net.datenwerke.async.configurations.*

def aService = GLOBALS.getInstance(DwAsyncService)
def poolName = 'myPool'

// get pool
def pool = aService.initPool(poolName, new SingleThreadPoolConfig())

def runA = {
  (1..10).each{	
	  tout.println "A says: $it"
      Thread.sleep(200)
  }
} as Runnable

def runB = {
  (1..10).each{	
	  tout.println "B says: $it"
      Thread.sleep(200)
  }
} as Runnable

def futureA = pool.submit(runA)
def futureB = pool.submit(runB)

// wait for tasks
futureA.get()
futureB.get()

aService.shutdownPool(poolName)

The initPool method takes a name and a configuration and creates a new pool for the given configuration. If a pool with the same name already exists, it will first shutdown the old pool. If we run this script we get the following result:

reportserver$ exec threadExample.groovy
A says: 1
A says: 2
A says: 3
A says: 4
A says: 5
A says: 6
A says: 7
A says: 8
A says: 9
A says: 10
B says: 1
B says: 2
B says: 3
B says: 4
B says: 5
B says: 6
B says: 7
B says: 8
B says: 9
B says: 10

There are two things to note. First, the printlns are not immediately pushed to the client. Currently ReportServer waits until the script terminates before it sends the result (which includes printlns) to the client. Secondly, the script executed the two loops not in parallel, but consecutively. This is, because of our choice of thread pool. We used a SingleThreadPoolConfig, which constructs a thread pool consisting of a single thread. Besides the SingleThreadPoolConfig you can use a FixedThreadPoolConfig which generates a pool with a fixed size. Thus, if we exchange the SingleThreadPoolConfig by

new FixedThreadPoolConfig(2)

and rerun our program, we get the following (expected) output:

reportserver$ exec threadExample.groovy
A says: 1
B says: 1
B says: 2
A says: 2
A says: 3
B says: 3
B says: 4
A says: 4
B says: 5
A says: 5
B says: 6
A says: 6
A says: 7
B says: 7
B says: 8
A says: 8
B says: 9
A says: 9
B says: 10
A says: 10