Using thread data



Because multiple threads can process simultaneously within a single request, applications must ensure that data from one thread does not improperly affect data in another thread. ColdFusion provides several scopes that you can use to manage thread data, and a request-level lock mechanism that you use to prevent problems caused by threads that access page-level data. ColdFusion also provides metadata variables that contain any thread-specific output and information about the thread, such as its status and processing time.

Thread scopes

Each thread has three special scopes:

  • The thread-local scope

  • The Thread scope

  • The Attributes scope

The thread-local scope

The thread-local scope is an implicit scope that contains variables that are available only to the thread, and exist only for the life of the thread. Any variable that you define inside the cfthread tag body without specifying a scope name prefix is in the thread local scope and cannot be accessed or modified by other threads.

To create a thread-local variable, assign the variable in the cfthread tag body without specifying a scope prefix, as in the following lines:

<cfset var index=1> 
<cfset index=1>

These two lines are equivalent, with one exception: If you use the var keyword, the assignment code must immediately follow the cfthread tag, before any other CFML tags.

The Thread scope

The Thread scope contains thread-specific variables and metadata about the thread. Only the owning thread can write data to this scope, but the page thread and all other threads in a request can read the variable values in this scope. Thread scope data remains available until the page and all threads that started from the page finish, even if the page finishes before the threads complete processing.

To create a Thread scope variable, in the cfthread tag body, use the keyword Thread or the name of the thread (for example, myThread) as a prefix. the following examples of creating a Thread scope variable are equivalent:

<cfset Thread.myValue = 27> 
<cfset myThread.myValue = 27>

To access a thread’s Thread scope variables outside the thread, prefix the variable with the thread’s name, as in the following example:

<cfset nextValue=myThread.myValue + 1>

Thread scope variables are only available to the page that created the thread or to other threads created by that page. No other page can access the data. If one page must access another page’s Thread scope data, you must place the data in a database or file and access it from there.

The Thread scope of each thread is a subscope of a special scope, cfthread, that lasts as long as the request, or until the last thread that it starts completes, whichever is longer. Thus, if you have two threads, myThread1 and myThread2, you can access their Thread scopes as cfthread.myThread1 and cfthread.myThread2 until all threads and the request complete. In most cases, there is no need to use the cfthread scope directly. However, you can use the cfthread scope name in either of the following situations:

  1. If you generate the thread name dynamically, you can avoid using the Evaluate function by using the cfthread scope with associative array notation, as the following code snippet shows:

    <cfset threadname="thread_#N#"> 
    ... 
    <!--- The following two lines are equivalent ---> 
    <cfset threadscopeForNthThread = cfthread[threadname] > 
    <cfset threadscopeForNthThread = Evaluate(threadname) >
  2. If you have a thread with the same name as a Variables scope variable, you can access that thread’s Thread scope only by prefacing the Thread name with cfthread. Otherwise, you access the Variables scope variable, or get an error.

The Attributes scope and thread attributes

The Attributes scope contains attributes that are passed to the thread, either individually or in the attributeCollection attribute. The Attributes scope is available only within the thread and only for the life of the thread.

ColdFusion makes a complete (deep) copy of all the attribute variables before passing them to the thread; therefore, the values of the variables inside the thread are independent of the values of any corresponding variables in other threads, including the page thread. For example, if you pass a CFC instance as an attribute to a thread, the thread gets a complete new copy of the CFC, including the contents of its This scope at the time that you create the thread. Any changes made to the original CFC outside the thread, for example, by calling a CFC function, have no effect on the copy that is in the thread. Similarly, any changes to the CFC instance in the thread have no effect on the original CFC instance.

Copying the data ensures that the values passed to threads are thread-safe, because the attribute values cannot be changed by any other thread. If you do not want duplicate data, do not pass it to the thread as an attribute or in the attributeCollection attribute. Instead, keep the data in a scope that the thread can access. An example of an object that should not be passed to the thread as an attribute is a singleton CFC that should never be duplicated. The singleton CFC must be kept in some shared scope and accessed by threads. For more information, see the Using other scopes.

Because ColdFusion copies all attributes by value, you can have multiple threads, for example, threads created dynamically in a loop, that use the same attribute names, but where each thread gets a different value, as shown in the following code excerpt, which creates separate threads to copy each of several files in a directory:

<cfloop query="dir"> 
    <cfset threadname = "thread_" & #i#> 
    <cfset i=i+1> 
    <cfthread name="#threadname#" filename="#dir.name#"> 
        <cffile action="COPY" source="#src#\#filename#" 
            destination="#dest#\#filename#\"> 
    </cfthread> 
</cfloop>

Using other scopes

Threads have access to all the ColdFusion scopes. All the threads run by a page share the same Variables and This scope. All the threads run in a request share the same Form, URL, Request, CGI, Cookie, Session, Application, Server and Client scopes. Be careful to lock access to these scopes if more than one thread could try to modify the data in the scopes; otherwise you can get deadlocks between threads. For more information, see Locking thread data and resource access.

Although a thread can access all the scopes, it might not be able to write to scopes like Session, Cookie, or Request after the request page processing completes.

Scope precedence

If you do not specify a scope prefix on a variable inside a cfthread tag body, ColdFusion checks scopes in the following order to find the variable:

  1. Function-local, in function definitions in the thread only

  2. Thread-local

  3. Attributes

  4. Variables

  5. Thread/cfthread

Other scopes are checked in the standard scope checking order.

Locking thread data and resource access

When an application uses multiple threads, be careful to ensure that the threads do not simultaneously attempt to use or modify shared resources that are not themselves thread-safe, including the following items:

  • If multiple threads modify a Variables or Request scope variable, use a Request scope lock to control access to the code that uses the variable to prevent deadlocks and race conditions. Similarly, use a Request scope lock around code that accesses built-in data structures or subscopes of the Variables scope, such as the Forms variable, that you change in multiple threads.

  • Multiple threads should not try to access any other shared resource simultaneously. For example, do not use the same FTP connection from multiple threads. To prevent this behavior, place the code that uses the resource in named cflock tags. Use the same name attribute for all cflock tags around code that uses a specific resource.

For more information on locking code, see cflock and Locking code with cflock.

Metadata variables

The Thread scope contains the following variables that provide information about the thread, called metadata.

Variable

Description

Elapsedtime

The amount of processor time that has been spent handling the thread.

Error

A ColdFusion error structure that contains the keys that you can access in a cfcatch tag. This variable has a value only if an unhandled error occurred during thread processing. For information on handling thread errors, see Handling ColdFusion thread errors.

Name

The thread name.

Output

Output text that the thread generates. Threads cannot display output directly. For more information see Handling thread output.

Priority

The thread processing priority, as specified when you created the thread.

Starttime

The time at which the thread began processing.

Status

The current status of the thread. For information on using the Status in an application, see Using the thread status.

As with other variables in the Thread scope, thread metadata is available to all of a page’s threads by specifying the thread name as a variable prefix. For example, the page thread can get the current elapsed time of the myThread1 thread from the myThread1.ElapsedTime variable.

The metadata is available from the time that you create the thread until the time when the page and all threads started on the page complete processing, even if the page finishes before the threads finish. This way, you can get thread output, error information, and processing information during and after the time when the thread is processing.