Saturday, August 29, 2009

Asynchronous ( Multi Threaded ) Application

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 {tag}CFTHREAD{tag} 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:

{tag}!--- Use a separate thread to perform file processing ---{tag}
{tag}cfthread action="run" name="threadFile" file="#myFile#"{tag}
{tag}cffile file="#ATTRIBUTES.file#" ...{tag}
{tag}/cfthread{tag}

Here a thread named 'threadFile' is spawned. An argument (the file to be processed) is passed to {tag}CFTHREAD{tag}, 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 {tag}CFTHREAD{tag}.

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:

{tag}cfthread action="join" name="threadFile"{tag}

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.

1 comment:

  1. This is really a major drawback that threads can only be spawned, there is no way to monitor spawned threads or wait for them to finish.And I agree that CFTHREAD{tag} is very strong feature that it support and used to perform many actions

    ReplyDelete