Other people have talked about CFTHREAD (see Ben Nadel's excellent post on the topic) so I'm not going to bother with a full description of the tag. Instead I wanted to show a quick example of where I've already put it to use in production, and I wanted to share a warning about how you have to be careful when using the tag.
BlogCFC makes use of a ping feature when posting entries. This lets you inform blog aggregators that you've written a new blog post. This way your latest devotional on Paris Hilton won't be missed by the masses.
Most aggregators simply take a simple HTTP hit, while others take a bit more work. BlogCFC supports simple HTTP pings, as well as Technorati, Weblogs, and Icerocket. The code is rather simple. You pass in the URLs you want to ping and this code block loops over them:
<cfloop index="aURL" list="#arguments.pingurls#">
<cfif aURL is "@technorati">
<cfset pingTechnorati(arguments.blogTitle, arguments.blogURL)>
<cfelseif aURL is "@weblogs">
<cfset pingweblogs(arguments.blogTitle, arguments.blogURL)>
<cfelseif aURL is "@icerocket">
<cfset pingIceRocket(arguments.blogTitle, arguments.blogURL)>
<cfelse>
<cfhttp url="#aURL#" method="GET" resolveurl="false">
</cfif>
</cfloop>
Rather simple, eh? Unfortunately this was also the slowest part of publishing a new blog entry. At times it would take up to 30 seconds to complete. This was an obvious place where CFTHREAD could help. I didn't need to even wait for the threads to finish. I could fire them off and forget about them. This was done by simply adding CFTHREAD around the code:
<cfthread action="run" name="#arguments.blogurl#_#arguments.blogtitle#_#aurl#" blogtitle="#arguments.blogtitle#" blogurl="#arguments.blogurl#" aURL="#aURL#">
<cfif aURL is "@technorati">
<cfset pingTechnorati(attributes.blogTitle, attributes.blogURL)>
<cfelseif aURL is "@weblogs">
<cfset pingweblogs(attributes.blogTitle, attributes.blogURL)>
<cfelseif aURL is "@icerocket">
<cfset pingIceRocket(attributes.blogTitle, attributes.blogURL)>
<cfelse>
<cfhttp url="#attributes.aURL#" method="GET" resolveurl="false">
</cfif>
</cfthread>
I supplied a unique name to my thread based on arguments passed in and the current URL being pinged. Notice too that I have to pass information into the inside of the thread. This was important since the code inside runs in its own scope.
And it's also where my warning came in. When I first wrote the code, I forgot to pass in the attributes. I just used cfthread and a name. The code inside the thread was erroring out and I never knew it. (Thanks to Todd for pointing this out to me.) It is something folks want to keep in mind when they use CFTHREAD. There is a status returned from threads that let you know if an error occurred. But since my threads were "fire and forget" (no join needed) I never realized this. Todd suggested a simple CFLOG at the end to ensure it worked. I used this for my debugging but removed it when done.
So - keep it in mind!
Oh - and the times? I love this. I mean, I really love this. It went from around 30 seconds per blog post to 1. Less than 1 but lets just say 1. Me Likey.
Archived Comments
That's some snappy threaded code there!
Well, I'm not sure it's snappy. To me its simple. I mean two tags. Thats it!! And if you ignore my screw up, look at the performance gain. Awesome. (Ok, so I may be a bit _too_ excited about this.)
It may be simple to code, but this new feature from CF8 is going to be really handy, and these demos you and Ben have been presenting are really helping us better understand, and realize the potential of all these new tags and functions.
CFThread definitely has some awesomesauce potential all around. I can definitely think of some things to use it for within a security framework.
What advantages or disadvantages does this have over using a asynchronous gateway? For instance, I use a gateway to process credit card transactions through authorize.net. Before nightly billing would take about 15 minutes to process/respond/process/respond, etc. Since I wrote the gateway nightly billing now takes only about 15 seconds to complete. Is there really any difference in performance, or functionality between the two?
Jason
http://www.lynda.com
I won't pretend to know the guts behind the two. I will say that coding wise, cfthread is a heck of a lot easier, not that the AsyncGateway is difficult.
We have a conference call with Ben today, so I'll make sure and add that question to the list(of many!) questions that we already have for him. I am curious to see if the underlying architecture is different, or if cfthread is just a fansy way of doing a async gateway :)
Jason
Let us know when you hear.
Ok, in's Ben's own words async gateways are basically a hack :) The two systems have nothing in common and for most purposes cfthread should be used. I'm going to go play our entire conversation back at half speed so I can understand him a little better, that boy talks a mile a minute! lol..
Jason
Let's also not forget that AsynGateways are Enterprise only, right? CFThread will be part of the core programming language and should be available to everyone. I have never been able to even play with Async gateways since I am on Standard.
@Ray, during the development process, it would probably be good to do a logging of some sort as the very last part of the CFThread body. I have done zero logging in my life, what might you recommend? CFLog? CFTrace? Something custom? I don't want to store to the system logs, just to a text file that I can monitor.
Ben - I don't think you always need to log. In cases when you do a join (ie, "Ok, wait for all threads to end."), the proper thing then is to check the statuses of the threads.
In cases of 'fire and forget', and your question of cflog versus cftrace... I don't think one is any better than the other... although wait - cftrace won't work as the result gets added to the debug stack and you won't see it because you request ends before the thread does. So for -that- case, I'd definitely use CFLOG. And you want something like the Eclipse Log View thing which does a 'tail' on the logs and makes it easier to monitor log files.
Yeah, it wouldn't make sense for the joined threads... I really have to take a look at CFLog. I have never used it. Don't know anything about it.
re: cflog
"What rolls down stairs alone or in pairs
Rolls over your neighbor's dog?
What's great for a snack and fits on your back?
It's Log, Log, Log!
It's Log, Log, it's big, it's heavy, it's wood.
It's Log, Log, it's better than bad, it's good!
Everyone wants a log! You're gonna love it, Log!
Come on and get your log! Everyone needs a Log!"
Sorry - couldn't resist.