在写桌面应用程序的时候,可能我们经常遇到一个问题,就是当我们的程序需要处理一个非常耗时的操作,这个时间可能是一分钟,五分钟甚至更长都有可能,当然这个时候我们不可能还用单线程来跑这样的操作,因为,主线程要负责维护一个消息队列,比如界面重绘,事件处理,如果此时采用主线程去处理这样的一个操作,将会使得界面重绘无法进行响应,从而使界面冻结,程序想死掉一样无法与用户进行下一步交互。
如果解决这个问题呢?首先我们想到的应该是采用多线程。
例如新建一个线程去处理这样一个操作:
Thread thread=new Thread(new ThreadStart(Function));
thread.start();
private void Function()
{
//处理这样的一个操作.
}
当然这样首先可以解决界面冻结,消息队列维护的问题,但是如果这样的操作可能有很多,而且是较为频繁,
如果还是采用原来的方法,每次都去新建一个线程,那样将会损耗大量的资源,因为线程之间的终止,新建,切换都有着大量的外部开销,如果线程数太多,将不利于应用程序的性能。
接着,我们可能又会换着另外一种方式来处理,马上的我们可能想到了线程池管理。
如下:
//处理一个方法,将其丢进线程池队列
ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(Function));
或许这样问题应该可以大概得到解决了,但是马上的我们又发现了新的问题,我们在新线程中的通讯无法马上响应到我们的主线程中来,以及不同线程之间访问一个控件所有可能产生的问题。
于是我们接着又用另外两种机制来解决这两个问题,用事件机制解决第一个问题,用控件委托调用解决第二个问题。
比如我们定义了一个Event,当新线程中发生了某个事件,在主线程中马上捕获这个事件,因为事件是消息队列机制的,所以程序不会“假死”,另外,我们在事件处理方法中又用委托来实现对控件的安全访问。
例如:
if(Control.InvokeRequired)
{
Control.Invoke(new XxDeleSetControl(SetControl),value);
}
else
{
//处理其他操作
Control.xx=value;
}
到这里,问题基本都解决了,接着我们再说一下异步方法委托调用。其实上面的程序还可以用异步委托来完成。
//异步方法委托调用的例子
class AsynDelegateInvokeDemo
{
public delegate int deleAsynCall();
public static void Main(string[] args)
{
//委托绑定异步方法,该方法必须和其委托定义具有相同签名
deleAsynCall dAsynCall = new deleAsynCall(Call);
//开始执行异步方法,这里有两种方式,轮询与回调,另外还有一种方式WaitHandle.WaitOne()
IAsyncResult ar = dAsynCall.BeginInvoke(null, dAsynCall);
//IAsyncResult ar = dAsynCall.BeginInvoke(new AsyncCallback(AsynFinish), dAsynCall);
Console.WriteLine("[main] Waiting for asynThread to finish");
//轮询检测异步方法是否执行完成
while(!ar.IsCompleted)
{
Console.Write(".");
Thread.Sleep(500);
}
int ret = dAsynCall.EndInvoke(ar);
Console.WriteLine("\r\n[AsynThread] The result is {0}", ret);
Console.ReadLine();
}
//假设这里是一个长操作
public static int Call()
{
int sum = 0;
while (sum < 99999)
{
sum += 2000;
Thread.Sleep(100);
}
return sum;
}
//完成回调函数
private static void AsynFinish(IAsyncResult ar)
{
deleAsynCall dAsynCall=(deleAsynCall)ar.AsyncState;
int ret = dAsynCall.EndInvoke(ar);
Console.WriteLine("\r\n[AsynThread] The result is {0}", ret);
}
}
如果解决这个问题呢?首先我们想到的应该是采用多线程。
例如新建一个线程去处理这样一个操作:
Thread thread=new Thread(new ThreadStart(Function));
thread.start();
private void Function()
{
//处理这样的一个操作.
}
当然这样首先可以解决界面冻结,消息队列维护的问题,但是如果这样的操作可能有很多,而且是较为频繁,
如果还是采用原来的方法,每次都去新建一个线程,那样将会损耗大量的资源,因为线程之间的终止,新建,切换都有着大量的外部开销,如果线程数太多,将不利于应用程序的性能。
接着,我们可能又会换着另外一种方式来处理,马上的我们可能想到了线程池管理。
如下:
//处理一个方法,将其丢进线程池队列
ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(Function));
或许这样问题应该可以大概得到解决了,但是马上的我们又发现了新的问题,我们在新线程中的通讯无法马上响应到我们的主线程中来,以及不同线程之间访问一个控件所有可能产生的问题。
于是我们接着又用另外两种机制来解决这两个问题,用事件机制解决第一个问题,用控件委托调用解决第二个问题。
比如我们定义了一个Event,当新线程中发生了某个事件,在主线程中马上捕获这个事件,因为事件是消息队列机制的,所以程序不会“假死”,另外,我们在事件处理方法中又用委托来实现对控件的安全访问。
例如:
if(Control.InvokeRequired)
{
Control.Invoke(new XxDeleSetControl(SetControl),value);
}
else
{
//处理其他操作
Control.xx=value;
}
到这里,问题基本都解决了,接着我们再说一下异步方法委托调用。其实上面的程序还可以用异步委托来完成。
//异步方法委托调用的例子
class AsynDelegateInvokeDemo
{
public delegate int deleAsynCall();
public static void Main(string[] args)
{
//委托绑定异步方法,该方法必须和其委托定义具有相同签名
deleAsynCall dAsynCall = new deleAsynCall(Call);
//开始执行异步方法,这里有两种方式,轮询与回调,另外还有一种方式WaitHandle.WaitOne()
IAsyncResult ar = dAsynCall.BeginInvoke(null, dAsynCall);
//IAsyncResult ar = dAsynCall.BeginInvoke(new AsyncCallback(AsynFinish), dAsynCall);
Console.WriteLine("[main] Waiting for asynThread to finish");
//轮询检测异步方法是否执行完成
while(!ar.IsCompleted)
{
Console.Write(".");
Thread.Sleep(500);
}
int ret = dAsynCall.EndInvoke(ar);
Console.WriteLine("\r\n[AsynThread] The result is {0}", ret);
Console.ReadLine();
}
//假设这里是一个长操作
public static int Call()
{
int sum = 0;
while (sum < 99999)
{
sum += 2000;
Thread.Sleep(100);
}
return sum;
}
//完成回调函数
private static void AsynFinish(IAsyncResult ar)
{
deleAsynCall dAsynCall=(deleAsynCall)ar.AsyncState;
int ret = dAsynCall.EndInvoke(ar);
Console.WriteLine("\r\n[AsynThread] The result is {0}", ret);
}
}