COMApartment object implements a thread with initialized COM in
it. By default single-threaded COM is initialized, through the
Pack1Creator object a multi-threaded apartment thread can be
created also (for advanced usage only).
Usually the script and the components created by it are created
in the same apartment where the script runs. As the apartments are
related to the threads the only safe and always guaranteed way to
run an object's method or another script in a separate thread is
to create it in a COM apartment independent of the apartment where
the main script runs. So one of the main purposes of the
COMApartment object is to support the COMThread
object. To illustrate this lets review this example:
We have a script we want to run in a separate thread. Lets
put it in a file called thread.js (JScript):
var Result = "";
function isSimple(n) {
var j;
for (j = 2; j <= n/2; j++) {
if ((n % j) == 0) return false;
}
return true;
}
for (i = 0; i < 40000; i++) {
if (isSimple(i)) {
Result += String(i) + " ";
State("Cur") = i + " of 40000";
}
}
This code calculates the simple numbers in the range from 1 to
40000. This will take considerable time and we want to start this
task in one ASP page and then check how it goes from time to time
by using another page. Start task page will contain code like this
(VBScript):
Set ap = Server.CreateObject("newObjects.utilctls.COMApartment")
Set host = ap.Creator.CreateObject("newObjects.Scphost.ScpMan2")
Set sf = Server.CreateObject("newObjects.utilctls.SFMain")
Set file = sf.OpenFile(Server.MapPath("thread.js"),&H40)
ap.Priority = -15
b = host.LoadEngine("JScript")
If Not b Then Response.Write "FAILED to load the engine<BR>"
b = host.AddText(file.ReadText(-2))
If Not b Then Response.Write "FAILED to load the script<BR>"
host.Add "State", Application
Set Application("Host") = host
Set Application("Ap") = ap
Set thread = Server.CreateObject("newObjects.utilctls.COMThread")
Set Application("Thread") = thread
thread.Activate
thread.Execute host, "Run"
Response.Write "Thread started<BR>"
Response.Write "Thread busy indication: " & thread.Busy & "<BR>"
Set thread = Nothing
In the code above we create a COMApartment object and we are
using the Pack1Creator object created automatically in it
(obtaining it from the Creator property). As this object is
created in the apartment any object created by it (unless the
created object is not registered as free-threaded) will be created
in the same COM apartment. The COMThread
object internally creates another apartment and calls from it our
script host component in the first apartment. Thus the both are
fully independent from the ASP page and the specifics of the ASP
engine (IIS or ALP) will not interfere.
To allow communication with the ASP pages we pass the
Application object to the script that will run in the thread. As
the Application object is independent from the current page it can
be accessed directly from the script in the thread and from the
page. The thread check page will have code like this:
Set thread = Application("Thread")
If thread.Busy Then
Response.Write "Thread still busy<BR>"
Response.Write "Progress: " & Application("Cur") & "<BR>"
Else
%>
<H4>Complete</H4>
<P><%= Application("Host").script.Result %></P>
<%
End If
It only checks the Application value where the progress is
reported (the "Cur" variable) and also checks if the
thread is still busy executing the script. If it is busy the
progress is reported, if the task is complete the result from the
script is extracted.
Notes about the sample:
As it can be seen above the execution of the script is done by
creating a ScriptManager2
component, loading the script from the file in it and forcing the
COMThread object to run it. The statement:
thread.Execute host, "Run"
actually means "execute method Run on the object host".
The method executed may not have arguments. As the method is
called from the separate thread maintained by the COMThread object
the ASP page execution will continue while the other script is
starting too.
The priority may puzzle you. We want to set lower
priority to the thread but instead we set the priority for the
COMApartment and not for the thread - why? (see COM
Apartments and threads) The call is initiated in the thread
maintained by the COMThread but it is actually executed in the
thread maintained by the COMApartment object because of the COM
synchronization mechanisms. So we tune the execution by tuning the
apartment and not the initiator thread!
This will be different if we execute free-threaded object's
method but scripts must be created in single-threaded apartments
because they must be called from the thread in which they are
created.