This attribute may be set on the xsl:for-each instruction. The value must be an integer. When this attribute is used with Saxon-EE, the items selected by the select expression of the instruction are processed in parallel, using the specified number of threads.

The threads are allocated on a round-robin basis: for example if threads="3" is specified, then the first item will be processed using thread 1, the second using thread 2, the third using thread 3, the fourth using thread 1 again, and so on. These threads are in addition to the main control thread (so there will be 4 threads in total). Before firing off the processing of the third item, the control thread will read off the results of processing the first item, and send them to the destination of the xsl:for-each instruction.

It is possible to specify saxon:threads="1". In this case all the items in the input will be processed sequentially, but asynchronously with the thread that reads the items in the input sequence.

Processing using multiple threads can take advantage of multi-core CPUs. However, there is an overhead, in that the results of processing each item in the input need to be buffered. The overhead of coordinating multiple threads is proportionally higher if the per-item processing cost is low, while the overhead of buffering is proportionally higher if the amount of data produced when each item is processed is high. Multi-threading therefore works best when the body of the xsl:for-each instruction performs a large amount of computation but produces a small amount of output.

It is possible to combine multi-threading with sorting. However, the input is first read and sorted synchronously, and the items in the sorted sequence are then processed in parallel.

The effect of using extensions that have side-effects (including saxon:assign) in a multi-threaded loop is undefined (and probably fatal).

Multi-threaded processing is available only with Saxon-EE. The attribute saxon:threads is ignored with a warning if Saxon-EE is not in use. Under Saxon-EE it may also be disabled using the configuration property ALLOW_MULTITHREADING. Multi-threaded processing is also disabled if code is compiled with tracing enabled, for example by using the -T option on the command line or by using an IDE debugger (this is because otherwise, the trace events would be hopelessly intermingled).

With multi-threaded processing, the output of different xsl:message instructions may appear in an unpredictable order. However, Saxon ensures that the xsl:message instruction is atomic, so one message will be completed before another starts. The same is true of the output from the trace() function call.