ColdFusion MX7 introduced the ability to asynchronously spawn ColdFusion requests using an event gateway. While many take advantage of this capability, it has some significant limitations, the biggest of which is that threads can only be spawned, there is no way to monitor spawned threads or wait for them to finish. (The other limitation is that the functionality is only available in ColdFusion Enterprise).
ColdFusion Scorpio provides far more sophisticated multi-threading capabilities via the new <CFTHREAD> tag. This tag is used to perform several actions:
- JOIN causes the current thread to wait until one or more specified threads have finished processing.
- RUN creates a new thread which starts processing immediately.
- SLEEP makes another thread pause for a specified number of milliseconds.
- TERMINATE kills another thread.
There are lots of use cases for this new functionality, but at a minimum there are two primary usage scenarios:
- Many requests process user submissions, for example, a user uploaded file. The way most ColdFusion applications work today is that the file is processed on the server (parsing it, converting it, saving it, etc.) while the user waits. But in truth, there is no reason users should have to wait for your application to finish its processing. A better user experience would be to receive the file in the action page, spawn a new thread to actually process it, and return control back to the user instantly. This creates a far more responsive user experience.
- Applications often have to perform multiple operations (perhaps updating database rows, writing log entries, generating an e-mail, firing server-side HTTP requests, and more). Most ColdFusion applications perform these tasks sequentially, one after the other, and then returning to the user when complete. But if the various operations are not actually dependent on each other, you could spawn a thread for each, having them execute concurrently, and if necessary waiting until they are complete to continue processing. The result is a faster application, as multiple operations are being performed concurrently.
The code to spawn a thread is very simple:
<!--- Use a separate thread to perform file processing --->
<cfthread action="run" name="threadFile" file="#myFile#">
<cffile file="#ATTRIBUTES.file#" ...>
</cfthread>
Here a thread named 'threadFile' is spawned. An argument (the file to be processed) is passed to <CFTHREAD>, and so that attribute is available within the thread in the ATTRIBUTES scope.
Within threads there are several important scopes. Any locally defined variables are implicitly thread local. THREAD is a special scope (a sub-scope of VARIABLES) that is available to all threads spawned by a single parent. ATTRIBUTES is used to access any variables passed as attributes to <CFTHREAD>.
The previous example spawns a thread that could continue processing long after the parent page terminates. If you needed to wait for a thread to complete you could use the following code:
<cfthread action="join" name="threadFile">
JOIN is used to wait for one or more threads to complete, and multiple thread names may be specified (as may a timeout value).
Once defined, the thread name can be accessed as a structure which exposes the following members:
- ELPASEDTIME is the amount of time since the thread was spawned.
- ERROR contains any error messages generated by the code in the spawned thread.
- NAME is the thread name.
- OUTPUT contains any generated output. This output will not be sent to the client, but parent page code can access the output which can then be used as needed.
- PRIORITY is the thread priority level (HIGH, LOW, NORMAL).
- STARTIME is the time the thread started.
- STATUS is the thread status (NOT_STARTED, RUNNING, TERMINATED, COMPLETED, WAITING).
So, to check that threads executed properly without errors, you could JOIN the threads, and then check STATUS to see if they completed. A status of TERMINATED means an error occurred (or that threads were explicitly terminated) in which case ERROR would provide details as to what happened.
Other options, keeping usability in mind, are using CFFLUSH to inform users about how their request is coming along, or using Ajax to submit the request you can let users carry on immediately and update them on their request via a pop-up or status panel on the page. Thoughts?
Could this be used to thread off a 'report generation' show a Report is being compiled page with a poll to check if the thread has completed (bad example).
In other words is the thread name only available in the variables scope (in effect isn't accessible once a request is complete).
Adam
The question to ask is whether the user NEEDS to be informed, in "real time" that the process ran correctly. Sometimes this is essential, sometimes this is just because it kinda made sense to have something at the bottom of the template popping up an alert saying "yep, all good" after the process. I think often the latter approach isn't really necessary: surely the assumption on a robust system should automatically be that it worked OK?
I think in a lot of situations the user (or not even that specific user, but an admin user) can simply be notified in exceptional circumstances. This can be effected by raising events when something goes wrong, and having a handler which takes an appropriate action (writing to a log; sending an email; interrupting the UI (either of the user initiating the task, or an admin user); [whatever]).
--
Adam
Mark, when CF8 is public so will be the docs.
Mike, versioning and editioning are not final or announced yet.
Adam R, absolutley. Or you could e-mail it to the user when done. Or display an alert on the next page the user displays (maybe the thread updates a flag in th users SESSION scope which is checked in an include used on each page). Or send am IM alert. ... Lots of options.
--- Ben
This might be a silly question, but I presume that this <cfthread> tag will allow the 'initial' thread to be completed, so that another user can use the 5-8 thread?
I'm still not sure i see how this syntax works. Is the #attributes.file# the .cfm file that's running? What if the file is a CFC?
Steve, you'd prob just do a <cfinvoke> or CreateObject() withijn the <cfthread> passing whatever you want on to it.
--- Ben
Thanks Ben. Since one might have lots of 'children' spawned threads, will CF 8 allow one to monitor these threads so to test the 'application' is scalable with alot more requests (end-users). It would be great to emulate 'x' number of users since these threads could be either/both 'database/web service' threads or OS threads <cffile>. I think this tag will be great if one can 'effectively' use it properly.
Pat
--- Ben
--- Ben
If a create a page (like a scheduled page) with some thread parallel processes, do I need to close page with a cfthread join of all opened threads?
I've some problem with a sms scheduler. Sometimes sending don't need. And sending mail too.
Thanks
p.s.: if you want I created an alternative to captcha, if you want you may contact me via mail, I don't want spam here :)