Application Domains and Threads
One aspect of how the isolation provided by an application domain
The fact that threads wander between application domains does not mean, however, that a given domain can have only one thread running in it at a given time. Just as with a Win32 process, several threads can be running in a single application domain
The second difference in the way threads are treated in the application domain model is that, unlike when you create a new process, creating a new application domain does not result in the creation of a new thread. When you create a new domain and transfer control into it by calling a method on a type in that domain, execution continues on the same thread on which the domain was created. To see how this works, let's return to the implementation of BoatRaceDomainManager.EnterBoat that we discussed in the previous section. As shown in the following code, the thread on which we're running switches into the new domain when we invoke the InitializeNewBoat method on the domain manager running in that domain. [View full width]
The high-level relationship between threads and application domains is shown in Figure 5-8. Figure 5-8. The relationship between threads and application domains
The .NET Framework provides some static
using System;
using System.Threading;
// Both return the domain in which the current thread is executing.
AppDomain currentDomain1 = AppDomain.CurrentDomain();
AppDomain currentDomain2 = Thread.GetDomain();
If you need to obtain the object that represents the current thread running in a domain, use the System.Threading.Thread.CurrentThread method. The System.AppDomain class also has a method called GetCurrentThreadId that returns an identifier for the current thread running in the calling domain. The relationship of this value to the thread identifiers provided in Win32 is undefined and can vary between different versions of the CLR. As a result, it's not safe to assume a direct mapping between these two concepts of thread identification. The CLR Thread Pool
If you're writing a multithreaded managed application, it's much easier to achieve high performance and scalability by using the CLR thread pool instead of creating, managing, and destroying threads yourself. There are a few reasons why this is the case. First, creating and destroying threads are expensive. By reusing the threads in the pool, you're able to minimize drastically the number of times a new thread is created. Second, creating the ideal
The CLR thread pool is represented by the System.Threading.ThreadPool class. The managed thread pool is similar in spirit and functionality to the thread pool provided by Win32. In fact, if you're familiar with the Win32 thread pool APIs, you'll feel comfortable with the members of the ThreadPool class in no time.
There is one managed thread pool per process. Because threads aren't confined to a particular application domain, the thread pool doesn't need to be either. This enables the CLR to optimize work across the entire process for greater overall performance. It's very common for a given thread pool thread to service
Multithread applications that use several application domains often follow a common pattern: first, a request comes in to execute some code. This request varies completely by scenario. For example, in SQL Server the request might result from building a plan to execute a query containing managed code. In the Internet Explorer case, the request might take the form of creating a new instance of a managed control on a Web page. Second, the application determines in which domain the request should be serviced. Depending on how your process is partitioned into multiple application domains, you can choose to execute your new request in an existing domain, or you can choose to create a new one. Next, an instance of the object representing the new request is created in the
To
using System;
using System.Threading;
namespace BoatRaceHostRuntime
{
// Some code omitted for clarity.
// This struct is used to pass request data to the delegate that
// will be executed by the thread pool.
struct ThreadData
{
public BoatRaceDomainManager domainManager;
public string assemblyFileName;
public string boatTypeName;
};
public class BoatRaceDomainManager : AppDomainManager,
IBoatRaceDomainManager
{
// This is the method that gets queued to the thread pool.
static void RunAsync(Object state)
{
ThreadData td = (ThreadData)state;
// Get the domain manager object out of the thread state
// object and call its InitializeNewBoat method.
td.domainManager.InitializeNewBoat(td.assemblyFileName,
td.boatTypeName);
}
public Int32 EnterBoat(string assemblyFileName, string boatTypeName)
{
// Create a new domain in which to load the boat.
AppDomain ad = AppDomain.CreateDomain(boatTypeName, null,
null);
BoatRaceDomainManager adManager =
(BoatRaceDomainManager)ad.DomainManager;
// Gather the domain manager and the name of the assembly
// and type into an object to pass to thread pool.
ThreadData threadData = new ThreadData();
threadData.domainManager = adManager;
threadData.assemblyFileName = assemblyFileName;
threadData.boatTypeName = boatTypeName;
// Queue a work item to the thread pool.
ThreadPool.QueueUserWorkItem(new WaitCallback(RunAsync),
threadData);
return ad.Id;
}
}
}
In this version of the implementation, we begin by declaring a structure called ThreadData that we'll use to pass the data to the new thread that we'll need to make the call. This data includes the application domain manager for the new domain and the name of the assembly and type containing the implementation of the boat. Next, we need to define a delegate to act as our thread proc. The RunAsync delegate takes a ThreadData structure, pulls out the application domain manager, and calls its InitializeNewBoat method. Now, after we create the new domain for the boat, we get its domain manager as before, but this time instead of calling InitializeNewBoat directly, we save the data needed to make the call into an instance of the ThreadData structure and invoke RunAsync asynchronously by queuing a request to the thread pool using ThreadPool.QueueUserWorkItem . Clearly, there is much more to the CLR thread pool than I've described here. For a more complete description, see the .NET Framework SDK reference material for System.Threading.ThreadPool . |