|
Multi-threaded applications use several building blocks,
including the following:
Starting threads in loops
Getting information about the thread processing status
Displaying thread results
Handling thread errors
Using database transactions with threads
Starting threads inside loopsBecause
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 (like the index variable, query name,
list item), 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 threadIndex attribute
value of the thread 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>
Using the thread statusThe 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 has 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:
A cfthread tag
with a terminate action stopped the thread.
An error occurred in the thread that caused it to terminate.
A ColdFusion administrator stopped the thread from the Server
Monitor.
|
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
specifies 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.
Handling thread outputTo prevent conflicts, only the page thread displays output.
Therefore, named threads have the following limitations:
ColdFusion places all output that you generate inside
a thread, such as HTML and plain text, or the generated output of
a cfoutput tag, in the Thread scope output metadata
variable. The page-level code can display the contents of this variable
by accessing the threadName.output variable.
All tags and tag actions that directly send output to the
client (instead of generating page text such as HTML output), do
not work inside the thread. For example, to use the cfdocument or cfreport tags
in a thread, specify a filename attribute; to use
a cfpresentation tag, use a directory attribute.
Handling ColdFusion thread errorsIf 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:
Use cftry/cfcatch tags or try/catch CFScript
statements in the cfthread body to handle the errors
inside the thread.
Handle the error outside the thread by using the thread error
information that is available to the page and other threads in the
Thread scope threadName.Error variable.
Application code can check this variable for error information.
For example, after you join to a thread that had an error, you could
check the threadname.status variable for
a value of terminated, which indicates that the
thread terminated abnormally. You could then check the threadName.Error variable
for information on the termination cause.
Handling database transactionsDatabase
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 place 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" ... />
|
|
|