基本概念
1. 进程
进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。进程之间是相对独立的,一个进程无法直接访问另一个进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行,Windows系统就是利用进程把工作划分为多个独立的区域的。进程可以理解为一个程序的基本边界。
2. 应用程序域
使用.NET建立的可执行程序 *.exe,并没有直接承载到进程当中,而是承载到应用程序域(AppDomain)当中。应用程序域是.NET引入的一个新概念,它比进程所占用的资源要少,可以被看作是一个轻量级的进程。
在一个进程中可以包含多个应用程序域,一个应用程序域可以装载一个可执行程序(.exe)或者多个程序集(.dll)。这样可以使应用程序域之间实现深度隔离,即使进程中的某个应用程序域出现错误,也不会影响其他应用程序域的正常运作。
当一个程序集同时被多个应用程序域调用时,会出现两种情况:
第一种情况:CLR分别为不同的应用程序域加载此程序集。
第二种情况:CLR把此程序集加载到所有的应用程序域之外,并实现程序集共享,此情况比较特殊,被称作为Domain Neutral。
3. 线程
线程(Thread)是进程中的基本执行单元,是程序执行流的最小单元。在进程入口执行的第一个线程被视为这个进程的主线程。在.NET应用程序中,都是以Main()方法作为入口的,当调用此方法时系统就会自动创建一个主线程。
线程主要是由CPU寄存器、调用栈和线程本地存储器(Thread Local Storage,TLS)组成的。CPU寄存器主要记录当前所执行线程的状态,调用栈主要用于维护线程所调用到的内存与数据,TLS主要用于存放线程的状态信息。
4. 三者的关系
进程、应用程序域、线程的关系如下图,一个进程内可以包括多个应用程序域,也有包括多个线程,线程也可以穿梭于多个应用程序域当中。但在同一个时刻,线程只会处于一个应用程序域内。
创建方法
1. 下面用火车票系统,介绍创建多线程的方法。
首先新建控制台程序项目,创建火车票类和人类:Ticket和Person.
class Ticket
{
private int count =100;
public int Count
{
get
{
return this.count;
}
}
public string GetTicket()
{
//while (true)
//{
this.count++;
Thread.Sleep(50);
this.count--;
//}
return "G" + this.count--;
}
}
class Person
{
private string name, id;
private int age;
public string Name
{
get
{
return this.name;
}
set
{
if (value.Length > 0 && value.Length < 8)
{
this.name = value;
}
else
{
throw new IndexOutOfRangeException("Length of name is out of 0~8.");
}
}
}
public int Age
{
get
{
return this.age;
}
set
{
if (value > 0)
{
this.age = value;
}
else
{
throw new IndexOutOfRangeException("Age must be more than 0.");
}
}
}
public string ID//身份证
{
get
{
return this.id;
}
set
{
if (value.Length == 18)
{
this.id = value;
}
else
{
throw new IndexOutOfRangeException("Lengh of ID must be 16.");
}
}
}
public Person(string nameOfPerson, int ageOfPerson, string idOfPerson)
{
this.name = nameOfPerson;
this.age = ageOfPerson;
this.id = idOfPerson;
}
}
其次在Program类中,创建公用的静态方法,后面创建多线程的方法会调用到。
class Program
{
static void BuyTicket(object state)
{
Ticket newTic = (Ticket)state;
BuyTicket(newTic);
}
static string BuyTicket(Ticket newTic)
{
lock (newTic)
{
ThreadMessage("Async Thread start:");
Console.WriteLine("Async thread do work!");
string message = newTic.GetTicket();
Console.WriteLine(message + "\n");
return message;
}
}
static void ThreadMessage(string data)
{
string message = string.Format("{0}\nCurrentThreadId is {1}", data, Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(message);
}
}
2. 通过Thread类创建
它能创建并控制线程,设置其优先级并获取其状态。通过ThreadStart来创建一个新线程是最直接的方法,这里不做介绍。ParameterizedThreadStart委托与ThreadStart委托非常相似,但ParameterizedThreadStart委托是面向带参数方法的。注意ParameterizedThreadStart 对应方法的参数为object,此参数可以为一个值对象,也可以为一个自定义对象。
这里介绍通过ParameterizedThreadStart创建。
①代码
static void Main(string[] args)
{
Ticket tic = new Ticket();
Person[] person = new Person[10]
{
new Person("Nicholas", 21, "000000000000000000"),
new Person("Nate", 38, "111111111111111111"),
new Person("Vincent", 21, "222222222222222222"),
new Person("Niki", 51, "333333333333333333"),
new Person("Gary", 28, "444444444444444444"),
new Person("Charles", 49, "555555555555555555"),
new Person("Karl ", 55, "666666666666666666"),
new Person("Katharine", 19, "777777777777777777"),
new Person("Lee", 25, "888888888888888888"),
new Person("Ann", 34, "99999999999999999"),
};
ThreadMessage("MainThread start");
Console.WriteLine();
Thread[] t = new Thread[person.Length];
for (int i = 0; i < person.Length; i++)
{
t[i] = new Thread(new ParameterizedThreadStart(BuyTicket));
t[i].Start(tic);
}
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Main thread do work!");
Thread.Sleep(200);
}
Console.ReadKey();
}
②运行结果
MainThread start
CurrentThreadId is 8
Async Thread start:
CurrentThreadId is 9
Async thread do work!
Main thread do work!
G100
Async Thread start:
CurrentThreadId is 10
Async thread do work!
G99
Async Thread start:
CurrentThreadId is 11
Async thread do work!
G