|Frontier Tutorials / Working With Threads / An Example|
As an example of a threaded application, let's look at a web search CGI that searches multiple commercial search engines and combines the results. (I'll not provide an entire CGI; that's left as an exercise for the reader. <evil laugh>)
First, we need a main thread, or executive. It takes the search terms from the paramTable and starts a separate thread for each search engine to be called.
queryThreadInfo = ThreadSafeGetUnique( tableType, "MultiQuery", @system.temp )
« Create Yahoo subtable
new( tableType, @queryThreadInfo^.yahoo )
« Copy params table for Yahoo query
queryThreadInfo^.yahoo.params = searchParams^
« Start the Yahoo query thread
queryThreadInfo^.yahoo.threadID = thread.easyCall( "queryYahoo", @queryThreadInfo^.yahoo )
« Create AltaVista subtable
new( tableType, @queryThreadInfo^.altaVista )
« Copy params table for AltaVista query
queryThreadInfo^.altaVista.params = searchParams^
« Start the AltaVista query thread
queryThreadInfo^.altaVista.threadID = thread.easyCall( "queryAltaVista", @queryThreadInfo^.altaVista )
(The ThreadSafeGetUnique script is documented on the Thread Utilities page.)
Then, for each search engine known to the CGI, we need a routine that constructs the query from parameters provided by the executive, calls tcp.httpClient to query the search engine, extracts the search results from the HTML page it received from the search engine into a table for use by the executive, and exits. These search-engine-specific routines are called queryYahoo and queryAltaVista in the code fragment above.
While the separate query threads are running, the executive has to bide its time, waiting for them to complete. The simple way to do this is for it to monitor a completion flag in the query threads' parameter tables.
yahooQueryCompleted = false
altaVistaQueryCompleted = false
queriesCompleted = false
while ( ! queriesCompleted )
thread.sleepFor( 1 )
if ( ! yahooQueryCompleted )
yahooQueryCompleted = queryThreadInfo^.yahoo.queryCompleted
if ( ! altaVistaQueryCompleted )
altaVistaQueryCompleted = queryThreadInfo^.altaVista.queryCompleted
queriesCompleted = yahooQueryCompleted && altaVistaQueryCompleted
The call to thread.sleepFor results in this loop executing once every second. We could simply call thread.exists to determine whether each thread has completed, but the queryCompleted flags allow the query threads to indicate when they have completed successfully. We should actually check both, so we will know if the thread terminates for some other reason, so we don't wait forever for the flag to be set. Also, since Web interactions can take a long time, we should have time-out logic that will kill a query thread if it takes longer than a user-specified time.
The final stage, then, will be to combine the results received and extracted from the various search engines into a single report page, and return it to the user.
The processing timeline then looks something like this:
Executive -- Startup Phase
Yahoo Query Thread
AltaVista Query Thread
This is, of course, a greatly simplified example. A complete multiple-search CGI would need at least the following additions: