Threads, Process, and AppDomains

简介:

Introduction: How It Works

The .NET Framework runs on top of the Windows operating system and as such, must be built using technologies that Windows can interface with. All managed modules and assembly files must use the PE (Portable Executable) format and must therefore be either a Windows executable or a Windows DLL. Further, the CLR and mscorlib.dll are the two most important components of the .NET Framework. The two of the most powerful resultant features of the .NET Framework are hosting and AppDomains. An Application Domain is a light-weight process that functions as a unit of isolation within that process to further function as a logical container that allows multiple assemblies to run within a single process which prevents them from directly accessing other assemblies’ memories. While considered a light-weight processAppDomains still offer many of the features that a Windows process offers: separate memory spaces and access to resources. AppDomains are actually more efficient than a process by enabling multiple assemblies to be run in separate application domains without the overhead of launching separate processes.

If you think about it, a Window process is normally written in C/C++. Most of the application DLLs and nearly all of the system DLLs are written in the native C language. But when developing the CLR Microsoft implemented it as a COM server contained inside a DLL: that is, Microsoft defined a standard COM interface for the CLR and assigned GUIDs (the unique identifiers that represent both the physical and the logical identifiers of a COM component) to this interface and the COM server. When you install the .NET Framework, the COM server representing the CLR is registered in the Windows registry just like any other COM component. The MSCorEE.h C++ header file ships with the .NET Framework SDK. This header file defines the GUIDs and the unmanaged ICLRRuntimeHost interface definition. Any Windows application can host the CLR but you don't instantiate the CLR COM server by calling CoCreateInstance; instead your unmanaged host should call the CorBindToRuntimeEx function, or another similar function, all of which are declared in the MSCorEE.h header file. The CorBindToRuntimeEx function is implemented in the MsCorEE.dll file that resides in the %systemroot%/system32 directory. This DLL then, is not a managed assembly like mscorlib.dll, but rather is called a shim. Its job is to determine which version of the CLR to create: either one for a server or one for a stand-alone desktop. The shim DLL does not contain the CLR COM server itself.

When the CorBindToRuntimeEx function is called, its arguments allow the host to specify which version of the CLR it would like to create, usually .NET 2.0 version 2.0.50727, as well as some other settings. Because the CorBindToRuntimeEx function gathers information about the number of CPUs installed on the machine, it can decide which version of the CLR to load -- the shim might not load the version that the host requested. This is why the CorBindToRuntimeEx function gathers this additional information. By default, when a managed executable starts, the shim examines the executable file and extracts the information indicating the version of the CLR that the application was built and tested with. The CorBindToRuntimeEx function returns a pointer to the unmanaged ICLRRuntimeHost interface. The hosting application can call methods defined by this interface to:

  • Set Host Managers: Tell the CLR that the host wants to be involved in making decisions related to memory allocations, thread scheduling/synchronization, assembly loading, etc.
  • Get Host Managers: Tell the CLR to prevent the use of some classes/members
  • Initialize and start the CLR
  • Load an assembly and execute code in it
  • Stop the CLR, thus preventing any managed code from running in the Windows process.

When the CLR COM server initializes, it creates an AppDomain, which then functions as that logical container for a set of assemblies. The first AppDomain is created when the CLR initialized is called the default AppDomain; this AppDomain is destroyed only when the Windows process terminates. But in addition to the default AppDomain, a host using either unmanaged COM interface methods or managed type methods can instruct the CLR to create additional AppDomains. Note that the entire purpose of the AppDomain is to provide isolation. Application domains are implemented in the .NET Framework using the System.AppDomain class. To use an application domain, you create an instance of the AppDomain class, and then execute an assembly within that domain:

 Collapse  |  Copy Code
using System;
class Test {
static void Main() {
AppDomain d = AppDomain.CreateDomain("NewDomain");
Console.WriteLine("Host domain:  "  + AppDomain.CurrentDomain.FriendlyName);
Console.WriteLine("Child domain:  "   + d.FriendlyName);
  }
}

Output:

 Collapse  |  Copy Code
c:\Windows\MICROS~1.NET\FRAMEW~1\V20~1.507>ADomain.exe
Host domain:  ADomain.exe
Child domain:  NewDomain

Here is an example of constructing an assembly name:

 Collapse  |  Copy Code
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;

public class Test {
public static void Main() {
 AssemblyName mscorlib = new AssemblyName();
            mscorlib.Name = "mscorlib";
            mscorlib.Version = new Version(2, 0, 0, 0);
            mscorlib.CultureInfo = CultureInfo.InvariantCulture;
            mscorlib.SetPublicKeyToken(new byte[] {
                0xb7, 0x7a, 0x5c, 056, 0x19, 0x34, 0xe0, 0x89 });
            mscorlib.ProcessorArchitecture = ProcessorArchitecture.X86;

            Console.WriteLine(mscorlib);
        }
}

When we compile this code, we get:

 Collapse  |  Copy Code
C:\Windows\MICROS~1.NET\FRAMEW~1\V20~1.507>AssemblyName.exe


mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c381934e089

And this is a basic look at unloading the assembly by creating an AppDomain:

 Collapse  |  Copy Code
public static void Main()
        {
            // Set up a new AppDomain, and load the DLL inside of it:
            AppDomain ad = AppDomain.CreateDomain("Isolated");

            //

            // We can then dump the contents of the AppDomain:
            AppDomain.Unload(ad);
        }
}

Now here is a test if System.dll is unloaded:

 Collapse  |  Copy Code
using System;
using System.Collections.Generic;
public class Test {
public static void Main()
        {
            Console.WriteLine("Before: {0}", Environment.WorkingSet);

            // Loads System.dll (if it's not already loaded):
            Type uriType = typeof(Uri); 
            Console.WriteLine("After loading System.dll: {0}", Environment.WorkingSet);

            // Loads System.dll into 10 new AppDomains:
            List
  ads = new List
 ();
            for (int i = 0; i < 10; i++)
            {
                AppDomain ad = AppDomain.CreateDomain(i.ToString());
                ad.DoCallBack(delegate { Type t = typeof(Uri); });
                ads.Add(ad);
            }
            Console.WriteLine("After loading System.dll into 10 AppDomains:  					{0}", Environment.WorkingSet);
            // Unload the appdomains and check the working set:
            foreach (AppDomain ad in ads)
                AppDomain.Unload(ad);
            Console.WriteLine("After unloading the AppDomains: {0}", 
						Environment.WorkingSet);
        }
}

The output:

 Collapse  |  Copy Code
Before: 5943296
After loading System.dll: 6107136
After loading System.dll into 10 AppDomains: 9572352
After unloading the AppDomains: 9732096

Processes in the .NET Framework correspond one to one with a process in Windows. A process’s primary purpose is to manage per-program resources; this includes a shared virtual address space among all threads running in that process, a HANDLE table, a shared set of DLLs (mapped into the same address space), and more other process-wide data that is stored in the Process Environment Block (PEB). Problems with one process will normally not affect other processes because of this isolation. But because of inter-process communication and system-wide shared resources – such as files, memory-mapped I/O, and named kernel objects – a process can interfere with another process. A single managed process (one which has loaded the CLR) can contain many AppDomains. It is suggested that the reader read up on and read some articles on the Thread Pool and the APM Programming Model. These topics involve a pool of threads in which there is one thread pool per process, and the ability to execute code asynchronously during a computation process. Below is some sample to exemplify getting information about a thread pool:

 Collapse  |  Copy Code
using System;
using System.Threading;
public class Test {
public static void Main()
        {
            // Retrieve the info:
            int minWorkerThreads, maxWorkerThreads, availableWorkerThreads;
            int minIoThreads, maxIoThreads, availableIoThreads;
            ThreadPool.GetMinThreads(out minWorkerThreads, out minIoThreads);
            ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxIoThreads);
            ThreadPool.GetAvailableThreads(
                out availableWorkerThreads, out availableIoThreads);

            // Now print it out:
            Console.WriteLine("Min/max/available worker threads: {0}/{1}/{2}",
                minWorkerThreads, maxWorkerThreads, availableWorkerThreads);
            Console.WriteLine("Min/max/available IO threads: {0}/{1}/{2}",
                minIoThreads, maxIoThreads, availableIoThreads);
        }
}

Compile and run this code to get information about the CLR's internal code that manages the thread pool. Now is a final example of getting all of the active processes listed while an instance of an unmanaged host, Internet Explorer, is running:

 Collapse  |  Copy Code
using System;
using System.Diagnostics;
using System.Threading;
public class Test {
 public static void Main()
        {
            Console.WriteLine("Current process:");
            PrintProcess(Process.GetCurrentProcess());

            Console.WriteLine("IE processes:");
            Process[] iexplorerProcs = Process.GetProcessesByName("iexplore");
            foreach (Process p in iexplorerProcs) PrintProcess(p);

            Console.WriteLine("All active processes:");
            Process[] allProcs = Process.GetProcesses();
            foreach (Process p in allProcs) PrintProcess(p);
        }
  private static void PrintProcess(Process p)
        {
            Console.WriteLine("  -> {0} - {1}", p.ProcessName, p.PeakWorkingSet64);
        }
 }

Examine the output:

 Collapse  |  Copy Code
Current process:
  -> Procc - 6279168
IE processes:
  -> iexplore - 70152192
All active processes:
  -> GoogleToolbarUser - 6385664
  -> WINWORD - 54874112
  -> GoogleToolbarNotifier - 8638464
  -> svchost - 73224192
  -> winlogon - 6696960
  -> spoolsv - 10362880
  -> svchost - 12292096
  -> svchost - 2220032
  -> cmd - 2166784
  -> audiodg - 15163392
  -> hpqste08 - 16248832
  -> svchost - 9318400
  -> notepad - 5001216
  -> svchost - 6324224
  -> svchost - 3137536
  -> SearchIndexer - 23379968
  -> csrss - 13602816
  -> hpwuSchd2 - 2953216
  -> WZQKPICK - 4448256
  -> taskeng - 5509120
  -> Procc - 6856704
  -> hpqtra08 - 11644928
  -> wininit - 4194304
  -> svchost - 30564352
  -> svchost - 3657728
  -> unsecapp - 4689920
  -> svchost - 99524608
  -> svchost - 7344128
  -> notepad - 5156864
  -> svchost - 6287360
  -> cmd - 2871296
  -> taskeng - 9412608
  -> explorer - 71344128
  -> svchost - 11722752
  -> lsm - 3989504
  -> SLsvc - 14442496
  -> MSASCui - 9420800
  -> ieuser - 8790016
  -> dwm - 4603904
  -> lsass - 8232960
  -> svchost - 111050752
  -> notepad - 5521408
  -> svchost - 3964928
  -> FlashUtil10b - 4689920
  -> services - 7118848
  -> WmiPrvSE - 5750784
  -> iexplore - 70152192
  -> smss - 778240
  -> svchost - 33824768
  -> csrss - 5222400
  -> System - 10354688
  -> svchost - 5210112
  -> ntvdm - 4370432
  -> Idle - 0

References

  • The CLR via C#, 2nd Edition, by Jeffrey Richter

History
















本文转自cnn23711151CTO博客,原文链接:http://blog.51cto.com/cnn237111/508444 ,如需转载请自行联系原作者




相关文章
|
6月前
|
机器学习/深度学习 TensorFlow 算法框架/工具
Gaussian Process
【6月更文挑战第14天】
55 4
|
Python
may have been in progress in another thread when fork() was called.
may have been in progress in another thread when fork() was called.
142 0
may have been in progress in another thread when fork() was called.
|
关系型数据库 Java MySQL