Damon Cooper of Adobe has posted a proof of concept for two new tags for the ColdFusion language: CFTHREAD and CFJOIN. These tags let you fire off events and simply forget about them. Right now there is an issue where you can't "completely" forget about them, but you can still run a large set of events asynchronously. Consider this example:
<cfif not isDefined("url.slow")>
<cfset good = true>
<cfelse>
<cfset good = false>
</cfif>
<cfhttp url="http://ray.camdenfamily.com/rss.cfm" result="result">
<cfset myrss = result.filecontent>
<cfset myrssParsed = xmlParse(myrss)>
<cfset myurls = xmlSearch(myrssParsed, "/rss/channel/item/link/text()")>
<cfset links = arrayNew(1)>
<cfloop index="x" from="1" to="#arrayLen(myurls)#">
<cfset arrayAppend(links, myurls[x].xmlvalue)>
</cfloop>
<cfif good>
<cfloop index="loopcounter" from="1" to="#arrayLen(links)#">
<cfset threadname = "thread_" & loopcounter>
<cfthread name="#threadname#">
<cfhttp url="#links[loopcounter]#" result="result">
<cffile action="write" file="c:\web\url#loopcounter#.txt" output="#result.filecontent#">
</cfthread>
</cfloop>
<cfloop index="loopcounter" from="1" to="#arrayLen(links)#">
<cfset threadname = "thread_" & loopcounter>
<cfjoin thread="#threadname#">
</cfloop>
<cfelse>
<cfloop index="loopcounter" from="1" to="#arrayLen(links)#">
<cfhttp url="#links[loopcounter]#" result="result">
<cffile action="write" file="c:\web\url#loopcounter#.txt" output="#result.filecontent#">
</cfloop>
</cfif>
<br>
Work Complete!<br>
What I've done is written two sets of code. One shows the use of cfthread/cfjoin, and one does not. The code downloads my RSS feed and gets an array of URLs. It then fetches each URL and saves it to a file. If you compare the "good" version versus the "bad" one (load the file with slow=true in the URL), you will see an incredible speed difference, and this is even with the bug. While the threads all run at the same time, you have to wait for the threads to end, whereas in the (hopefully) final version, you would not need to.
Archived Comments
whoa thats crazy, so if I understand you, you can wrap things in cfthread and have them process in paralell with whatever else is going on on that cfm? I didn't quite get what CFJOIN does though. still very cool :)
Yes to your first question. All the CFHTTPs ran at once.
To your second question, join means, "Wait here till thead named X is done."
Again, the need for CFJOIN in my example is a bug. Well, not a bug. If you wanted to show a "All COmplete" as I do, then it is good. But if you wanted to fire and forget, you can't do that quite yet.
I saw this one coming.
When I attended the BlueDragon session at CFUnited, they introduced the cfthread tag. I was really excited about the cfthread tag because I have applications that could really benefit from this.
My first thought was "I wonder how long until Macrodobe implements these tags." And here we are. I'm glad that they're finally working on it.
Somewhere, Adam Smith is smiling. Competition is good.
Technically, you could already do threading, at least in Enterprise by using the asynch event gateway. What these tags do is make that functionality more accessable.
Asynch gateways allow threading, but this allows a much more controlled way to manage it. With asynch gateways you kind of lob them out there on their own and carry on.
One thing notable about this is that where some of us already experience issues with max threads filling up, this idea sure could make that happen faster if you were not careful in how you implemented it.
This is a great tag but more than that it's wonderful to see something from BlueDragon coming over to CF MX. Incompatibility like this helps strengthen the CFML language. It could so easily go the other way if both companies add new tags that are alien to each other. Definitely good news for the CF community. I hope it makes it into CF 8.
Incidentally, I used CFEXECUTE to fire off new CF threads in CF5. e.g. to generate 1000's of emails as a background task without making the user wait. I don't think CF7 can run cfml scripts from the command line in the same way?
No, but you can do command line URL hits, so that would be one option.
Raymond,
Be very cafeful w/these tags. My findings are that in your example the variable "loopcounter" is not thread safe and you'll end up with various results. Looping over the <cfthread /> at this point seems very instable. See my comments on Damon's original blog post for more indepth information.
Thanks for the warning. Once they get these issues cleared up, this is going to rock.
@ Raymond
This tag will indeed rock. It can already be useful, but just not in a looping context at the moment--there's too much risk of having variables overriden.
However, the tags seem to work great if they each have individual hard coded procedures. The one thing I'd do is to create a separate structure for each thread (make sure to use duplicate() on complex variables) and store values inside that structure. That way you create a pseudo scope to keep values from overlapping.
-Dan
As a companion blog post, folks can check out my post regarding this functionality in Blue Dragon 7. That post is located here:
http://www.trajiklyhip.com/...
I know this is an old post, and I know that Dan G. Switzer, II already pointed this out, but I'd like to reinforce that loopcounter is not a thread safe variable to reference inside the loop. You don't have a guarantee that you'll see the increment of this variable for each thread. This is still the case in the launch version of ColdFusion 8, it's not just a beta bug.
For more information, including details on how to handle it, see my post about it: http://www.bandeblog.com/20...
(P.S. your comment form rejects email addresses with a + before the @ sign, which are valid)
Maybe I'm missing the obvious Eric, but why not just pass the value to the thread? That wasn't available when this POC came out, but CF8 lets you do
cfthrad name="foo" randomarg="2"
Inside the thread you can use attributes.randomarg.
From the docs:
ColdFusion makes a complete (deep) copy of all the attribute variables before passing them to the thread, so the
values of the variables inside the thread are independent of the values of any corresponding variables in other
threads, including the page thread. Thus, the values passed to threads are thread safe because the attribute values
cannot be changed by any other thread
That's a good observation Ray. The cf docs didn't make it obvious to me that you could pass custom attributes to the thread. I'd seen the verbiage about making a deep copy of the attributes before execution, but this didn't imply to me that this one tag accepts custom attributes (are there any other CF tags which do other than custom tags?).
Updated my blog entry to point this out, thanks for the tip.