Multithreaded applications use several building blocks, including the following:
Because threads run asynchronously, page level variables can change during thread execution. As a result of this behavior, if you start threads inside a cfloop, and code inside the threads uses the value of the loop iterator (the index variable, query name, list item, etc.), you must pass the loop iterator to the thread as an attribute.
The following example shows the use of threads inside a loop. It uses an indexed cfloop tag to start five threads. Each thread gets the current loop index value in a threadIndex attribute. The thread adds an array entry with the thread's threadIndex attribute value and the current value of the page cfloop index, pageIndex. After joining the threads, the page displays the array contents. When you run the example, particularly if you run it multiple times, you see that at the time the thread saves data to the array, the value of pageIndex has incremented past the threadIndex value, and multiple threads often have the same pageIndex value; but the multiple threads always have the correct threadIndex value.
<cfloop index="pageIndex" from="1" to="5"> <cfthread name="thr#pageIndex#" threadIndex="#pageIndex#" action="run"> <cfset Variables.theOutput[threadIndex]="Thread index attribute:" & threadIndex & " Page index value: " & pageIndex> </cfthread> </cfloop> <cfthread action="join" name="thr1,thr2,thr3,thr4,thr5" timeout=2000/> <cfloop index="j" from="1" to="5"> <cfoutput>#theOutput[j]# <br /></cfoutput> </cfloop>
The Thread scope status metadata variable lets the page, or any other thread started by the page, determine the status of any thread. The page processing code can then take a necessary action, for example, if the thread has terminated abnormally or might be hung. The status variable can have the following values:
Value |
Meaning |
---|---|
NOT_STARTED |
The thread has been queued but is not processing yet. |
RUNNNG |
The thread is running normally. |
TERMINATED |
The thread stopped running as a result of one of the following actions:
|
COMPLETED |
The thread ended normally. |
WAITING |
The thread has run a cfthread tag with action="join", and one or more of the threads being joined have not yet completed. |
Applications can check the thread status to manage processing. For example, an application that requires results from a thread might specify a time-out when it joins the thread; in this case, it can check for the COMPLETED status to ensure that the thread has completed processing and the join did not just result from a time-out. Similarly, an application can check the status value of threads that might not start or might not complete normally, and terminate it if necessary. The example in Ending a thread checks thread status and terminates any threads with RUNNING or NOT_STARTED status.
To prevent conflicts, only the page thread displays output. Therefore, named threads have the following limitations:
threadName
.output variable.
If an error occurs in a thread, page-level processing is not affected, and ColdFusion does not generate an error message. If you do not handle the error by using a try/catch block in the thread code, the thread with the error terminates and the page-level code or other threads can get the error information from the thread metadata Error variable and handle the error appropriately.
You cannot use page- or application-based error handling techniques to manage errors that occur during thread execution. For that reason, you cannot use the cferror tag or the onError application event handler for thread errors. Instead, use either of the following techniques:
Database transactions cannot span threads. For example, consider a page with the following structure:
<cftransaction> <cfthread name ="t1" ...> <cfquery name="q1" ...> ... </cfquery> </cfthread> <cfquery name="q2" ...> ... </cfquery> <cfthread action="join" name="t1" ... /> </cftransaction>
In this case, query q1 is not included in the transaction that contains query q2. To include both queries in the transaction, you must put the complete transaction in a single thread, using a structure such as the following:
<cfthread name ="t1" ...> <cftransaction> <cfquery name="q1" ...> ... </cfquery> <cfquery name="q2" ...> ... </cfquery> </cftransaction> </cfthread> <cfthread action="join" name="t1" ... />