Threading
Last updated
Last updated
Ortus Solutions, Corp
CFML allows you create asynchronous threads so you can execute a body of code in a separate thread. This is achieved via the cfthread
tag & the thread
construct. Threads are independent streams of execution, and multiple threads on a page can execute simultaneously and asynchronously, letting you perform asynchronous processing in CFML. CFML code within the cfthread
tag body executes on a separate thread while the page request thread continues processing without waiting for the cfthread
body to finish. You can allow the thread body to continue executing in the background or you can wait for it to finish.
IMPORTANT: You cannot spawn a thread from within a thread in any of the CFML engines.
This approach is very very simplistic, if you want more control of your asynchronous programming aspects then we can move into leveraging CFML Future's via the runAsync()
function or parallel Java streams using the cbStreams project. Please see our Asynchronous Programming section for information on advanced asynchronous programming.
Please note that once you get into concurrency you will start to get many headaches. Your code must be thread safe, appropriate locking must be in place and overall concurrency based programming will be needed on any shared resource. It is also very difficult to debug because you are no longer in the same thread and a writedump() + abort
combo usually goes into ether. Logging will be your best friend and outputting logs to the console for debugging purposes.
Here are some utility functions to assist with logging:
systemOutput( obj, addNewLine:boolean, doErrorStream:boolean)
- Writes the given text or complex objects to the output or error stream. Complex objects are outputted as JSON. (Lucee-only) https://cfdocs.org/systemoutput
cfdump( var="text", output="console" )
- Send the variables to the output console, even complex variables. Complex objects are outputted as JSON. https://cfdocs.org/cfdump
cflog( text, log, file, type ) or writeLog()
- Leverage the ColdFusion engine's logging facilities to send typed messages. https://cfdocs.org/cflog
Writing thread
is extremely easy, just use the construct, give it a few attributes an boom you are in multi-threaded land. In tags you can use the <cfthread>
tag.
Examples:
Because multiple threads can process simultaneously within a single template request, applications must ensure that data from one thread does not pollute or affect data in another thread. CFML 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. CFML also provides metadata variables that contain any thread-specific output and information about the thread, such as its status, processing time and much more, which extremely useful.
Thread local
scope
Thread
scope
Attributes
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. This exactly the same as the function local
scope. Any variable that you define inside the thread
body without specifying a scope name prefix is in the thread local scope and cannot be accessed or modified by other threads.
Thread
ScopeThe 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. So be careful with what you store in this scope or you can create memory leaks.
To write to this scope you can use the thread
scope or actually the name of the thread as well, which is pretty cool.
To read from this scope outside the thread
construct you can use the name of the thread or the thread scope, but you must reference which thread scope within it using it's name: thread.myThreadName
Thread scoped 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, ever! If one page must access another page's Thread scope
data, you must place the data in a shared location such as a shared scope or a file or database.
The thread body executes in isolation, but sometimes you need to be able to pass certain data that the thread body cannot access. In this case, we will pass them via the thread
construct as a name-value pair. The CFML engine will then place those in the thread's attributes
scope so they can be used for the life of the thread.
WARNING
All variables passed as attributes will be duplicated by the engine (deep copy). That's right! A raw duplicate() will be executed for the passing variable. If it is an array or struct, the entire collection will be duplicated. If it is an object with an object graph, the ENTIRE object graph will be duplicated.
In some cases, that's ok, but it can be expensive and produce results that are not expected. Only pass variables to the attributes that you know and are ok with being duplicated. 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 but use a shared scope instead that the thread body can access.
That's it for threading. Such a simple but powerful construct built right into the CFML language. Like mentioned before, if you need much more granular control or advanced ways to do asynchronous programming, go to our section on running async code fluently.