使用线程的几个原因, 假设从应用程序中进行网络调用需要一定时间,用户不希望分割用户界面,并长期等待,希望同时执行其他一些操作,或者取消发送给服务器的请求。这些可以用线程实现。
对于所有需要等待的操作,如文件,数据库或者网络访问带来的等待,可以启动一个新线程,同时完成其他任务。一个进程的多个线程可以同时运行与不同CPU,或者多核CPU的不同内核。 如果线程访问相同数据,必须实现同步机制,否则极易出错。
运行在服务器上的应用程序,带带客户请求的线程,成为侦听器线程,只要接收到请求,就把他传给另一个工作线程,之后继续与客户通讯。侦听器会立即返回,接收下一个客户发送的下一个请求。
进程包含资源: window句柄,文件系统句柄或其他内核对象。每个进程都分配了虚拟内存,一个进程至少包含一个线程,操作系统会调度线程,线程有一个优先级,正在处理的程序的位置计数器,一个存储局部变量的栈。 每个线程都有自己的栈,但程序代码的内存和堆有一个进程的所有线程共享,因为寻址都是在内存中完成,这使得线程间通讯非常的快。
1. 异步委托: 创建线程的一个简单的方法是定义委托, 并异步调用它,委托是方法的类型安全的引用,Delegate类还支持异步调用方法,在后台,委托类Delegate会创建一个执行任务的线程;委托使用线程池来完成异步操作。测试一下委托的异步特性, 从一个需要时间完成的方法测试,TasksAWhile()方法需要经过第2个变量
class Program
{
static void Main(string[] args)
{
//TakesAWhile(1, 10000);
TakesAWhileDelegate d1 = TakesAWhile; // 使用委托调用一次这个方法
d1(2, 1000);
Console.ReadKey();
}
public delegate int TakesAWhileDelegate(int data, int ms);
static int TakesAWhile(int data, int ms)
{
Console.WriteLine("TakesAWhile started");
Thread.Sleep(ms);
Console.WriteLine("TakesAWhile completed");
return ++data;
}
}
接下来如果后台执行这个委托的操作,在10秒的漫长等待中,在屏幕上打印"."应该如何操作呢? 使用"投票"技术,通过IAsyncResult检验委托是否完成了。 而开始调用委托,这是用委托的方法: BeginInvoke(),而BeginInvoke返回类型就是IAsyncResult,而BeginInvoke总是包含两个参数(object, AsyncCallback), IAsyncResult类型的返回对象,包含一个属性"IsCompleted", 用来判断委托是否完成任务;
只要委托没有完成,主线程就继续执行while循环。接下来程序改为:
static void Main(string[] args)
{
//TakesAWhile(1, 3000);
TakesAWhileDelegate d1 = TakesAWhile;
//开始调用委托:
IAsyncResult ar = d1.BeginInvoke(1, 10000, null, null);
while (!ar.IsCompleted)
{
Console.Write(".");
Thread.Sleep(50); //每次打点等待0.05秒
}
int result = d1.EndInvoke(ar); // 结束调用时要输入IAsyncResult类型的参数
Console.WriteLine("Result: {0}", result);
}
以上是通过委托线程调用方法的基本例子,可以看到主线程和委托线程同时运行,在委托线程执行完毕后,主线程就停止循环。
处理等到线程完全结束,还可以通过等待句柄来判断是否结束,如果不用ar.IsCompleted, 主线程的循环会一直继续,如果设置循环一直跑下去,即使后台线程结束,主线程也不会结束。这是就需要ar.AsyncWaitHandle.WaitOne(time, false)来进行干预:
static void Main(string[] args)
{
//TakesAWhile(1, 3000);
TakesAWhileDelegate d1 = TakesAWhile;
//开始调用委托:
IAsyncResult ar = d1.BeginInvoke(1, 10000, null, null);
//while (!ar.IsCompleted)
while(true)
{
Console.Write(".");
if (ar.AsyncWaitHandle.WaitOne(50, true)) //50表示异步执行的时间间隔,true表示在等待之前是否退出同步域;
{
Console.Write(".");
//Thread.Sleep(50); //每次打点等待0.05秒
break;
}
}
int result = d1.EndInvoke(ar); // 结束调用时要输入IAsyncResult类型的参数
Console.WriteLine("Result: {0}", result);
Console.ReadKey();
}
接下来看看回调方法的作用,作为等待结果的第三种方法,定义回调函数,并且在BeginInvok()方法的第三个参数中,可以传递一个满足AsyncCallback委托的需求方法
static void Main(string[] args)
{
TakesAWhileDelegate d1 = TakesAWhile;
d1.BeginInvoke(1,3000, TakesAWhereCompleted, d1);
for (int i = 0; i < 100; i++)
{
Console.Write("+");
Thread.Sleep(50);
}
}
static void TakesAWhereCompleted(IAsyncResult ar)
{
if (ar == null) throw new ArgumentNullException("ar");
TakesAWhileDelegate d1 = ar.AsyncState as TakesAWhileDelegate;
Trace.Assert(d1!=null, "Invalid object type");
int result = d1.EndInvoke(ar);
Console.WriteLine("result: {0}",result);
}
public delegate int TakesAWhileDelegate(int data, int ms);
static int TakesAWhile(int data, int ms)
{
Console.WriteLine("TakesAWhile started");
Thread.Sleep(ms);
Console.WriteLine("TakesAWhile completed");
return ++data;
}