完成各种各样的信息处理工作室人们开发应用软件最重要的目的之一。有些信息处理工作需要较长的时间来完成,比如在包含很多文件的硬盘上查找特定的文件,在这种情况下让应用程序停下来等待这一项工作完成是不现实的。应该让其在后台运作,这是应用程序还可以被用户操作,这需要异步编程的知识。
.NET的异步编程技术主要分为:
1,使用IAsyncResult的异步编程模式:又分为基于委托和世界是用基类库中的相关组件两种类型;
2,基于事件的异步编程(EAP)。
开发一个EAP组件相当繁琐,实际中并不常用,所以主要学习上面的第一种模式编程和了解如何使用现有的EAP组件。
程序的同步执行和异步执行
在许多程序中代码时顺序执行的,如果在代码中调用了一个方法,则必须等待此方法的所有代码均执行完毕之后,才能回到原来的地方执行下一行代码。这种程序运行的方式称为“同步”;
简单例子查看文件容量:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace CalculateFolderSizeNoAsync
{
class Program
{
//计算指定文件夹的总容量
private static long CalculateFolderSize(string FolderName)
{
if (Directory.Exists(FolderName) == false)
{
throw new DirectoryNotFoundException("文件夹不存在");
}
DirectoryInfo RootDir = new DirectoryInfo(FolderName);
//获取所有的子文件夹
DirectoryInfo[] ChildDirs = RootDir.GetDirectories();
//获取当前文件夹中的所有文件
FileInfo[] files = RootDir.GetFiles();
long totalSize = 0;
//累加每个文件的大小
foreach (FileInfo file in files)
{
totalSize += file.Length;
}
//对每个文件夹执行同样的计算过程:累加其下每个文件的大小
//这是通过递归调用实现的
foreach (DirectoryInfo dir in ChildDirs)
{
totalSize += CalculateFolderSize(dir.FullName);
}
//返回文件夹的总容量
return totalSize;
}
static void Main(string[] args)
{
long size;
string FolderName;
Console.WriteLine("请输入文件夹名称(例如:C:\\Windows):");
FolderName = Console.ReadLine();
size = CalculateFolderSize(FolderName);
Console.WriteLine("\n文件夹{0}的容量为:{1}字节\n", FolderName, size);
Console.ReadKey();
}
}
}
对于层次很深文件数量很大的文件,需要很长时间,才能检索完毕。在这段时间内,应用程序只能等待(用户有可能感觉程序死了,没有任何提示)下面应用异步方法会有不一样的体验。
将上述程序改为异步执行:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace CalculateFolderSizeNoAsync
{
class Program
{
//计算指定文件夹的总容量
private static long CalculateFolderSize(string FolderName)
{
if (Directory.Exists(FolderName) == false)
{
throw new DirectoryNotFoundException("文件夹不存在");
}
DirectoryInfo RootDir = new DirectoryInfo(FolderName);
//获取所有的子文件夹
DirectoryInfo[] ChildDirs = RootDir.GetDirectories();
//获取当前文件夹中的所有文件
FileInfo[] files = RootDir.GetFiles();
long totalSize = 0;
//累加每个文件的大小
foreach (FileInfo file in files)
{
totalSize += file.Length;
}
//对每个文件夹执行同样的计算过程:累加其下每个文件的大小
//这是通过递归调用实现的
foreach (DirectoryInfo dir in ChildDirs)
{
totalSize += CalculateFolderSize(dir.FullName);
}
//返回文件夹的总容量
return totalSize;
}
public delegate long CalculateFolderSizeD(string FolderName);
static void Main(string[] args)
{
long size;
string FolderName;
CalculateFolderSizeD d = CalculateFolderSize;
Console.WriteLine("请输入文件夹名称(例如:C:\\Windows):");
FolderName = Console.ReadLine();
//通过委托异步调用静态方法CalculateFolderSize
IAsyncResult ret = d.BeginInvoke(FolderName, null, null);
Console.WriteLine("正在计算中,请耐心等待……");
//阻塞,等到调用完成,取出结果
size = d.EndInvoke(ret);
//size = CalculateFolderSize(FolderName);//不用这个了
Console.WriteLine("\n文件夹{0}的容量为:{1}字节\n", FolderName, size);
Console.ReadKey();
}
}
}
异步编程的基础是委托与多线程,上面示例中定义了一个委托,编译器在编译上述委托定义语句是,会自动产生一下类的模板:
public sealed class CalculateFolderSizeD : MulticastDelegate
{
public CalculateFolderSizeD (Object target,int methodPtr)
{ .... }
public virtual long Inoke (string FolderName)
{ .... }
public virtual IAsyncResult BeginInvoke(string FolderName, AsyncCallback callback, object asyncState)
{ .... }
public virtual long EndInvoke(IAsyncResult result)
{ .... }
}
当使用同步模式直接运用位委托调用方法时:
<span style="white-space:pre"> </span>CalculateFolderSizeD d = CalculateFolderSize;
<span style="white-space:pre"> </span>d(FolderName);
编译器会调用转换为相应的Invoke方法,一步是相反。BeginInvoke方法参数的第一部分是定义委托是确定的方法签名中的参数列表,第二个参数callback是当异步调用结束时自动回调的方法,第三个参数asyncSate用于向第二个参数所确定的callback回调方法提供额外的信息。
注意:可以查看一下IAsyncResult接口的定义。(对理解内容有所帮助)
#region 程序集 mscorlib.dll, v4.0.0.0
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1\mscorlib.dll
#endregion
using System.Runtime.InteropServices;
using System.Threading;
namespace System
{
// 摘要:
// 表示异步操作的状态。
[ComVisible(true)]
public interface IAsyncResult
{
// 摘要:
// 获取用户定义的对象,它限定或包含关于异步操作的信息。
//
// 返回结果:
// 用户定义的对象,它限定或包含关于异步操作的信息。
object AsyncState { get; }
//
// 摘要:
// 获取用于等待异步操作完成的 System.Threading.WaitHandle。
//
// 返回结果:
// 用于等待异步操作完成的 System.Threading.WaitHandle。
WaitHandle AsyncWaitHandle { get; }
//
// 摘要:
// 获取一个值,该值指示异步操作是否同步完成。
//
// 返回结果:
// 如果异步操作同步完成,则为 true;否则为 false。
bool CompletedSynchronously { get; }
//
// 摘要:
// 获取一个值,该值指示异步操作是否已完成。
//
// 返回结果:
// 如果操作完成则为 true,否则为 false。
bool IsCompleted { get; }
}
}
下一节会用到这些内容!