<cffunction name="multiThreadDemo" access="public" returntype="struct"> <!

--- This is what we do normally inside cffunction because of our Framework, but you dont have to do it like this ---> <!--- Specify a local scope variable---> <cfset var local = structNew()> <!--- specify a result struct to use as a return variable ---> <cfset local.result = structNew()> <!--- obviously, you might have this coming from somewhere different ---> <cfset local.ds = "theDatasource"> <!--- This is fed into the recordset-retrieving cfquery. -1 means all records ---> <cfset local.maxRecordSetSize = -1> <!--- This is the number of threads that you want to run at a time. We callt he collection of threads a Spool. This doesn't change any JRun or JVM settings, so be aware of that. In other words, if you have JVM setting that specifies a max of 10 threads and you set the maxConcurrentThreads variable to 11 then only 10 will be created to start with, there will then be a wait until the next single thread is created ---> <cfset local.maxConcurrentThreads = 5> <!--- This is the max number of records from your data query that will be processed inside each thread. Think of it as the control on how long you want each thread to run for. For a record set of 26471, I used a batch size of 200 with 5 concurrent threads ---> <cfset local.recordBatchSize = 200> <!--- depends very much on the size of your record set and how much processing you are going to do ---> <cfsetting requesttimeout="360"> <cfsavecontent variable="local.result.output"> <cfquery datasource="#local.ds#" name="local.data" maxrows="#local.maxRecordsetSize#"> select fields from tableName </cfquery> <!--- calculations to work out from/to ---> <cfset local.currentThreadNumber = 0> <cfset local.recordStartPointAddition = 0> <cfset local.totalNumberOfThreads = Ceiling(local.data.recordCount / local.recordBatchSize)> <!--- numberOfSpools is the number of sets of <maxConcurrentThreads> threads that will be run---> <cfset local.numberOfSpools = Ceiling(local.totalNumberOfThreads / local.maxConcurrentThreads)> <cfoutput><h1>#local.data.recordCount# records to update</h1></cfoutput> <cfloop from="1" to="#local.numberOfSpools#" index="local.spoolI"> <cfset local.threadList = "">

<cfoutput><h2>Spool #local.spoolI#</h2></cfoutput> <cfloop from="1" to="#local.maxConcurrentThreads#" index="local.threadI"> <cfset local.queryFrom = ((local.threadI1)*local.recordBatchSize)+1 + local.recordStartPointAddition> <cfset local.queryTo = Min(local.queryFrom + (local.recordBatchSize-1), local.data.recordCount)> <!--- you create threads called 'thread<i>' where i is the variable local.currentThreadNumber ---> <cfset local.currentThreadNumber = local.currentThreadNumber + 1> <!--- make sure you need to do something ---> <cfif local.queryFrom le local.queryTo> <cfoutput> <h3>Creating thread #local.currentThreadNumber# running records #local.queryFrom# to #local.queryTo#</h3> </cfoutput> <!--- create and run the thread. Pass in the parameters and data ---> <cfthread action="run" name="thread#local.currentThreadNumber#" data="#local.data#" queryFrom="#local.queryFrom#" queryTo="#local.queryTo#" > <!--- it is all in the thread's local scope anyway ---> <cfset local = structNew()> <!--- anything with thread. as the scope can be retrieved once the thread is Completed---> <cfset thread.recordTotal = 0> <cftry> <cfloop query="attributes.data" startrow="#attributes.queryFrom#" endrow="#attributes.queryTo#"> <!--- CUSTOM Processing Code Goes Here ---> </cfloop> <!--- keep running totals of how many records have been processed ---> <cfset thread.recordTotal = attributes.queryTo-attributes.queryFrom+1> <cfcatch type="any"> <!--- errors need passing out of the thread otherwise you end up clueless---> <cfset thread.cfcatch = cfcatch> </cfcatch> </cftry> </cfthread> <!--- keep a record of what thread numbers have been run during this Spool ---> <cfset local.threadList = ListAppend(local.threadList, local.currentThreadNumber)> </cfif> </cfloop>

<cfif ListLen(local.threadList)> <cfoutput><h6>Rejoin threads #local.threadList#</h6></cfoutput> thread---> <!--- Add up all the records-processed values from each

<cfset local.threadRecordTotal = 0> <cfloop list="#local.threadList#" index="local.threadI"> <cfset local.theThreadName = "thread#local.threadI#"> <!--- Wait for the thread to finish ---> <cfthread action="join" name="#local.theThreadName#"/> <cftry> <!--- Weird way of getting thread value out, but the trouble is threads are NOT structures. If anyone has a better way of doing this, please let me know!---> <cfset local.theThreadTotal = Evaluate('#local.theThreadName#.recordTotal')> <cfset local.threadRecordTotal = local.threadRecordTotal + local.theThreadTotal> <!--- Make sure the thread is really dead---> <cfthread action="terminate" <cfset "#local.theThreadName#" = ""> <cfif local.theThreadTotal eq 0> <!--- this is likely to run if the thread errored and exited ---> <cfoutput> <p><strong>Thread #local.theThreadName# doesn't seem to have done anything</strong></p> <cfdump var="#Evaluate('#local.theThreadName#')#"> </cfoutput> </cfif> <cfcatch type="any"> <cftry> <cfdump var="#cfcatch#"> <cfdump var="#Evaluate('#local.theThreadName#')#"> <cfcatch type="any"> <cfoutput><p>Unrecoverable error on thread #local.theThreadName#</p></cfoutput> </cfcatch> </cftry> </cfcatch> </cftry> </cfloop> <cfoutput><h4>Spool #local.spoolI# looped through #local.threadRecordTotal# records</h4><hr/></cfoutput> <cfset local.recordStartPointAddition = local.recordStartPointAddition + local.threadRecordTotal>


</cfif> </cfloop> </cfsavecontent> <cfreturn local.result> </cffunction>