AdobeStock_455007340

Multi-Threaded Application Development In Scorpio

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. 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:




Here a thread named ‘threadFile’ is spawned. An argument (the file to be processed) is passed to , 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 .
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:

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.

22 responses to “Multi-Threaded Application Development In Scorpio”

  1. tony of the weeg clan Avatar
    tony of the weeg clan

    holy threadtastics! ben keep the good news coming broseph!

  2. Gary Fenton Avatar
    Gary Fenton

    I’m trying to see how cfthread can help one of my apps which can take 2 to 10 secs to process some pages – lots of db transactions, log writing, web services, etc, all in one page request. While some tasks can be spawned off as separate threads (others must be done sequentially) what is there to gain using cfthread if CF has to report back to the user to say if everything went well or not? The user will still have to wait for the processes to complete. On a busy site wouldn’t creating more threads take a share of cpu resource and effectively slow down other user’s requests?
    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?

  3. Mark Ireland Avatar
    Mark Ireland

    Where are the CF8 docs?

  4. Mike Avatar
    Mike

    Is this going to be an enterprise only function like the event gateways in CF7, or will this be available for both versions?

  5. Adam Reynolds Avatar
    Adam Reynolds

    Ben,
    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

  6. Adam Cameron Avatar
    Adam Cameron

    Gary:
    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

  7. Ben Forta Avatar
    Ben Forta

    Gary, as Adam C wrote, it’s going to depend on the application. If you need while-you-wait processing, then don’t use threads. If not, then you can.
    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

  8. Patrick Whittingham Avatar
    Patrick Whittingham

    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?

  9. Steve Nelson Avatar
    Steve Nelson

    Man I could make use of this. Lately I’ve been experimenting with writing stock market prediction software, my scripts often take upwards of 30-40 minutes to process.
    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?

  10. Ben Forta Avatar
    Ben Forta

    Patrick, sure, if the spawned thread is still running, and the parent finishes, then that parent thread is freed up for other requests.
    Steve, you’d prob just do a <cfinvoke> or CreateObject() withijn the <cfthread> passing whatever you want on to it.
    — Ben

  11. Steve Nelson Avatar
    Steve Nelson

    Oh baby! That’s sweet!

  12. Patrick Whittingham Avatar
    Patrick Whittingham

    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

  13. Ben Forta Avatar
    Ben Forta

    Pat, yes, the server monitor will let you monitor threads. Also, if you spawn more threads that CF will support, then they will just queue up like any other CF requests.
    — Ben

  14. Eric Avatar
    Eric

    Will the server’s request timeout apply to threads? For instance, if a normal cfm page times out after 30 seconds, would the thread processing also time out after 30 seconds? This is very cool functionality, thanks for sharing.

  15. Ben Forta Avatar
    Ben Forta

    Eric, I believe so. Unless you use a processing directive to change that, just liek any other request.
    — Ben

  16. Merlinox Avatar
    Merlinox

    Is optional to insert a cfthred join after a cfthred?
    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 🙂

  17. Anthony Webb Avatar
    Anthony Webb

    Is there a way for us guys using cfthread with cf8 standard (and dont have the monitoring capability) to show a list of outstanding threads queued up and know when they are all caught up? I have an app that runs on a schedule and does some work in the background, cfthread is perfect, but it would be nice to know what the queue looked like at any given point.

  18. Ben Forta Avatar
    Ben Forta

    Stephan, sorry, I don’t understand your issue. Preface local variables with var (or LOCAL in CF9) and problem solved.
    — Ben

  19. Stephan Verbeeck Avatar
    Stephan Verbeeck

    CFTHREAD has proven itself to be a very dangerous feature.
    Because items created or used in CFFUNCTION are by default common, this is also true when a CFFUNCTION is called from inside a CFTHREAD.
    This causes totally unpredictable results. In the past year we have spend >6 months debugging CFTHREAD related code.
    With the introduction of CFFUNCTION ColdFusion should have used the approach of the Python scripting language (in a Python functions all refered items are local unless you declare them as being global). Now we have to use external tools like VarScoper to keep our code free from bugs. Until now without any success. We simply can’t get our code bugfree anymore.
    This is a serious problem and we are seriously considering to stop using ColdFusion for application development. Even if using another language takes 4 times as much work, we would save time because of the debugging cyclus now taking 2/3 of the development time.

  20. Stephan Verbeeck Avatar
    Stephan Verbeeck

    Another inconvenient limitation is that it is not allowed to spawn additional threads from within a thread.
    I can not imagine why anybody came up with that idea.
    I can tell you that WE NEEDED IT and that it took us a lot of time and effort to program a framework to get around this limitation.
    We also had to program the framework to report errors and concatinate the output when joining threads, anybody any idea why ColdFusion does not handle this by default?
    What I do like is the way how every thread has its own dedicated database connection (this is were we get our performance from).

  21. Stephan Verbeeck Avatar
    Stephan Verbeeck

    Hi Ben, well one of the problems is that what you ask is almost impossible to do as there are so many CF-tags that created variables (cfparam, cfquery, cfhttp…) that it is an almost impossible task to do that. Also the mechanism that searches through the list of scopes (arguments -> variables -> url -> form -> cgi -> cookie …) is NOT multithreading save.
    We have identified >20 bugs that we could solve by putting "variables." in front of function names because otherwise the thread now and then claimed that the function or variable did not exist while a value was assigned to it a line higher successfully. Sorry but something went seriously wrong in release 8.2 CFTHREAD is unstable. No doubt about it!
    Also when you assign values to a DIFFERENT variables that both exists in the variable scope from within a thread from different threads that a values is added to a hashing table (variables) and this TOO is not multithreading-safe. So locking is not a solution because you have to lock changes to ANYTHING in the same scope. Not just when the change happens to the same variable. I think CFTHREAD was released to soon and hope that meanshile most of these bugs have been identified and silently rectified in v9.0. That I hope! Otherwise the future looks very grim as this is a batle that no developer can hope to win.

  22. Rupesh Kumar Avatar
    Rupesh Kumar

    Stephan,
    can you send me your mail Id so that we can discuss the issues related to cfthread? You can mail me at "rukumar at adobe".
    Rupesh

Leave a Reply