进程
- 启动一个程序时,系统在内存中创建了一个新的进程(process).
- 进程就是一组资源,他们构成了一个正在运行的程序。这些资源包括虚拟地址空间,文件句柄以及程序启动需要的其他东西载体。
线程
- 在进程中,系统创建了一个叫做线程(thread)的内核对象,线程体现了一个程序的真实执行情况。
- 程序开始运行时,系统在线程中开始执行Main方法的第一条语句。
- 线程时由系统负责调度的处理器(不是进程)上的执行单位.
同步编程
- 程序只有一个线程,从程序的第一行语句到最后一行语句顺序执行。
异步编程
- 程序发起多个线程,理论上是同一时间执行。(实际上,不是完全真的在同一时间执行,处理器会按照时间片轮流执行)
多线程处理
- 在程序中使用多个线程叫做多线程处理。增加了程序的负荷和额外的复杂度。
- 为了帮我们降低创建和销毁线程相关的成本,CLR为每一个进程维护了一个线程池(thread pool). 一开始进程的线程时空的,如果进
程使用的线程被创建,并且完成了线程的执行,它不会被销毁,而是加入到进程的线程池中。之后,如果进程需要另外一个线程,CLR会
从池中还原一个线程,就节省了很多时间。
- 多线程处理对GUI编程非常重要,保持良好的用户体验。
异步编程模式
- C#有个简单易用的机制用于异步执行方法,那就是使用委托。
- 如果委托对象在调用列表中只有一个方法,它就可以异步执行这个方法。
- 委托类有两个方法,叫做BeginInvoke 和 EndInvoke
* 使用方法如下:
当调用委托的BeginInvoke方法是,它开始在线程中的独立线程上执行引用方法,并且立即返回原始线程。原始线程可以继续,而引用
方法会在线程池中并行执行。
当程序希望获取已完成的异步方法的结果时,可以检查BeginInvoker返回的IAsyncResult的IsCompleted 属性,或调用委托的
EndInvoke方法来等待委托完成.
- 异步方法调用的3种标准模式.
(1)等待-直到完成(wait-until-done).
-> 在发起了异步方法以及做了一些其他处理之后,原始线程就中断并且等异步方法完成之后再继续。
(2)轮询(polling)
-> 原始线程定期检查发起的线程是否完成,如果没有则可以继续做一些其他的事情.
(3)回调(callback)
-> 原始线程一直执行,无需等待或检查发起的线程是否完成。
BeginInvoke 和 EndInvoke
- BeginInvoke 从线程池中获取一个线程并且在新的线程开始时运行引用方法。
- BeginInvoke 返回给调用线程一个实现IAsyncResult接口的对象,这个接口引用包含了异步方法的当前状态,原始线程然后可以继续
执行。
delegate long MyDel( int first, int second); //声明委托
...
static long Sum(int x, int y) { ...} //定义方法来匹配委托。
...
MyDel del = new MyDel(Sum); //创建委托对象.
//调用委托对象的BeginInvoke方法并且提供了参数3,5, callback(设null), state (设null) .
IAsyncResult iar = del.BeginInvoke (3,5,null,null);
EndInvoke 方法用来获取由异步方法调用返回的值,并且释放线程使用的资源。
long result = del.EndInvoke(iar);
EndInvoke 提供了从异步方法调用的所有输出,包括ref 和out 参数. 如果委托的引用方法有ref 或out 参数, 他们必须包含在
EndInvoke 的参数列表中.
long result = del.EndInvoke(out someInt, iar);
等待-直到-结束(wait-untill-done)模式
- 原始线程发起一个异步方法的调用,做一些其他的处理,然后停止并等待,一直到线程结束。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
//声明委托
delegate long MyDel(int first, int second);
namespace ConsoleApplication1
{
class Program
{
static long Sum(int x, int y) //声明方法匹配委托,用于异步执行。
{
Console.WriteLine(" Inside Sum");
Thread.Sleep(3000);
return (x + y);
}
static void Main()
{
MyDel del = new MyDel(Sum);
Console.WriteLine("Before BeginInvoke");
IAsyncResult iar = del.BeginInvoke(3, 5, null, null); // 开始异步调用
Console.WriteLine("After BreginInvoke");
Console.WriteLine("Doing stuff");
long result = del.EndInvoke(iar); //等待结束并获取结果
Console.WriteLine("After EndInvoke:{0}", result);
}
}
}
输出:
Before BeginInvoke
After BeginInvoke
Doing stuff
Inside Sum
After EndInvoke: 8 //等待3秒后,显示结果.
轮询(polling)模式
-原始线程发起了异步方法的调用,做一些其他事情,然后用IAsyncResult对象的IsComplete属性来定期检查开启的线程是否完成。如果
异步方法已经完成,原始线程就调用EndInvoke 并继续。否则,做一些其他的处理,然后再来定期检查。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
//声明委托
delegate long MyDel(int first, int second);
namespace ConsoleApplication1
{
class Program
{
static long Sum(int x, int y) //声明方法匹配委托,用于异步执行。
{
Console.WriteLine(" Inside Sum");
Thread.Sleep(5000);
return (x + y);
}
static void Main()
{
MyDel del = new MyDel(Sum);
Console.WriteLine("Before BeginInvoke");
IAsyncResult iar = del.BeginInvoke(3, 5, null, null); // 开始异步调用
Console.WriteLine("After BreginInvoke");
//检查异步方法是否完成
while (!iar.IsCompleted)
{
Console.WriteLine("Not done, wait for 1 more second.");
//处理一些其他事情
Thread.Sleep(1000);
}
Console.WriteLine("Done");
long result = del.EndInvoke(iar); //等待结束并获取结果
Console.WriteLine("After EndInvoke:{0}", result);
}
}
}
输出:
Before BeginInvoke
After BeginInvoke
Not done, wait for 1 more second.
Inside Sum
Not done, wait for 1 more second.
Not done, wait for 1 more second.
Not done, wait for 1 more second.
Not done, wait for 1 more second.
Done
After EndInvoke:8
回调(callback)模式
-一旦初始线程发起了异步方法,就自己管自己,不再考虑同步。当异步方法调用结束后,系统调用一个用户自定义的方法来处理结果,
并且调用委托的EndInvoke 方法.
-BeginInvoke的参数列表中个最后的两个额外参数被回调方法用作:
第一个参数,callback,是回调方法的名字.
第二个参数,state,可以使null 或者传入回调方法的一个对象的引用。通过IAsyncResult参数的AsyncState属性来获取这个对象,参
数的类型是object.
回调方法
void asyncCallback(IAsyncResult iar)
IAsyncResult iar1=del.BeginInvoke(3,5,new AsyncCallback(CallWhenDone),null);
或 IAsyncResult iar1=del.BeginInvoke(3,5,CallWhenDone,null);
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.Remoting.Messaging; //调用AsyncResult类型.
//声明委托
delegate long MyDel(int first, int second);
namespace ConsoleApplication1
{
class Program
{
static long Sum(int x, int y) //声明方法匹配委托,用于异步执行。
{
Console.WriteLine(" Inside Sum");
Thread.Sleep(500); //备注A
return (x + y);
}
static void CallWhenDone(IAsyncResult iar)
{
Console.WriteLine(" Insdie CallWhenDone.");
AsyncResult ar = (AsyncResult)iar;
MyDel del = (MyDel)ar.AsyncDelegate;
long result = del.EndInvoke(iar);
Console.WriteLine(" The result is : {0}", result);
}
static void Main()
{
MyDel del = new MyDel(Sum);
Console.WriteLine("Before BeginInvoke");
IAsyncResult iar = del.BeginInvoke(3, 5, new AsyncCallback(CallWhenDone), null); // 开始异步调用
Console.WriteLine("Doing more work in main,ie. wait for 1 more second.");
Thread.Sleep(1000);
Console.WriteLine("Done with Main.");
}
}
}
输出:
Before BeginInvoke
Doing more work in main, ie. wait for 1 more second.
Inside Sum
Inside CallWhenDone.
The result is : 8
Done with Main.
备注A: 如果将此处修改为: Thread.Sleep(5000);
则输出:
Before BeginInvoke
Doing more work in main, ie. wait for 1 more second.
Inside Sum
Done with Main.
计时器
- 计时器在每次时间到期之后调用回调方法。回调放必须是TimerCallback委托形式的.
void TimerCallback(object state)
- 当计时器到期后,系统会从线程池中的线程上开启一个回调方法,提供state对象作为其参数,并且开始运行.
- 可以设置的计时器的一些特性如下:
dueTime:是回调方法首次被调用之前的时间。如果dueTime被设置为特殊的值Timeout Infinite ,则计时器不会开始。如果被设置为0,
回调函数会被立即调用.
period 是两次成功调用回调函数之间的时间间隔。如果它的值设置为Timeout.Infinite,回调在首次被调用之后不会再被调用.
state 可以使null 或在每次回调方法执行时要传入的对象的引用。
Timer类的最为常用的构造函数如下:
Timer(TimerCallback callback, object state, uint dueTime, uint period)
创建Timer对象的一个实例:
//MyCallback是回调的名字,someObject是传给回调的对象, 2000毫秒后第一次调用,以后每隔1000毫秒调用一次.
Timer myTimer = new Timer( MyCallback, someObject, 2000, 1000 ) ;
Timer对象被创建后,我们还可以使用Change方法来改变 dueTime 或 period.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
int TimesCalled = 0;
void Display(Object state)
{
Console.WriteLine("{0} {1}", (string)state, ++TimesCalled);
}
static void Main()
{
Program p = new Program();
Timer myTimer = new Timer(p.Display, "Processing timer event", 2000, 1000);
Console.WriteLine("Timer Start.");
Console.ReadLine();
}
}
}
输出:
Timer Start.
Processing timer event 1
Processing timer event 2
Processing timer event 3
Processing timer event 4
Processing timer event 5
Processing timer event 6
...
(每隔一秒会增加一行输出 )
.NET BCL 还提供了其他几个计时器类.
System.Windows.Forms.Timer -> 主要用于GUI编程.
- 这个类在Windows应用程序中使用,可以定期把WM_TIMER消息放到程序的消息队列中.
- 当程序从队列获取消息后,它会在主用户接口线程中异步处理。
System.Timers.Timer
- 包含很多成员,可以通过属性和方法来操作计时器。
- 这个计时器可以运行在用户接口线程或工作者线程上。