最近有一个项目,需要对文件进行对比,说白一点就是数据库里存了文件的物理路径,你要用程序查一下,这些文件是否真实存在。当时没有想太多,就写了一个程序,发现程序直接就卡死。因为数据量比较大,上百万条,而且你也不知道这个数据对比到哪里了。 这时候就想到了用异步来处理,上网查了一下。网上也有很多文章,但是我看了好几天才看明白,本人比较笨,而且以前没有用过异步。
为了让自己记得更准备和扎实一些,我就把我学习的一些经历和方法记录下来,如果有朋友感觉不对,或是有更好的方法,希望告知,我在这里十分感谢了。
这里面有一些基本的定义,我就找了一些网上的文章的片断,我个人认为很不错。很好理解
一,我们为什么要用异步
离你很远的地方有个很漂亮的女网友,你想知道她到底对你如何,你通过邮政礼仪,寄给她一支玫瑰(相当于异步调用)。因为回执要等很多天,所以,你这 些天不必整天傻等回执,可以照样吃饭工作(处理些简单的事务),当然,如果你这些天又泡到个MM,这时你就要选择,如果也用异步的方法,当然没问题,因为 哪个回 执先回来,先处理那个,但如果你对后面这个MM采用同步方式,那你只能解决掉这个同步的MM后,才能处理前面那个回执,不管那个回执何时到达。
二,委托是什么?
委 托其实就等于C++里面的函数指针,是指向函数地址的变量。我想,也许是为了更加面向对象,并且避免指针的滥用而导致系统错误,C#引用了委托这个概念。 其实不管是事件,还是委托,大家都可以把它们看成跟类一样的东西,都需要声明,都需要进行实例化,并且其实例可以看成引用类型的对象。这样的话,更加容易 理解很多觉得很神秘的东西。
委托就是声明一个“函数指针”,但这个“指针”必须要声明其指向函数的形式。
知道了这些,我们就用一个小例子来看一下他是怎么完成的。我做的例子比较简单,就是一个算一个从1加到10的方法。
在前台我建立了三个控件,二个textbox,一个button,分别是txtNum用来显示计算过程的页面。txtInfo用来显示一些相关的信息,主要是让大家看一下他是否进行了异步操作。btnAdd就是一个计数的按钮了
//定义一个没有返回值的委托,处了多了一个delegate是不是和定义函数没有什么区别
public delegate void getNums();
/// <summary>
/// 无返回值调用方法,用来计数,这里我用了一个Thread.sleep(500)这个大家都应该明白是什么意思,就是让它暂停半秒钟
/// 只有这样,我们才能看到它的变化。
/// *****这里我要说明一点的是,这个函数是要被委托调用的,所以它要与委托的定义方式一样。代不代参数和有没有返回值都要与委托一///样********
/// </summary>
public void GetNum()
{
for (int i = 1; i <= 10; i++)
{
this.txtNum.Text = (Convert.ToInt32(this.txtNum.Text.Trim()) + i).ToString();
Thread.Sleep(500);
}
this.txtInfo.Text = "结束计数";
}
好,委托我们定义好了,要计数的方法函数我们也定义好了,下面就要在点按钮的时候来运行它了。
大家可能都知道,在我们进行异步操作的时候,程序已经都给我们建立好了,一些前面代begin的方法。基本上你要进行异步的时候,都会有相对应与同步的方法一样的只是前面加了一个begin的方法。最常见的,就是file.beginRead
private void btnAdd_Click(object sender, EventArgs e)
{
this.txtInfo.Text = "开始计数";
//这里初始化一下委托,这里要说明的是,委托中调用的函数不用加()只要一个名字就行了。
getNums gn = new getNums(GetNum);
//委托中的BeginInvoke里面有二个参数
//一个回调函数callback .类型:System. AsyncCallback
//工作流已完成后要调用的方法。
//另一个是一个object类型的参数。在后面一节中我们会用到它
//因为我们没有任何的返回值,所以BeginInvoke里面就是给null
gn.BeginInvoke(null, null);
//这里你可以再做一些别的
}
好了,我们试一下吧。看一下结果,
这时候一定会出现错误,说txtNum控件不在此线程中。这是因为你异步调用的时候调用了不在此线程中建立的控件。才会报这个错误。我们用一个最简单的方法来解决,但是我不建议大家使用。以后我们会说到底用什么方法比较好。
我们在
public Form1()
{
InitializeComponent();
//就是加入这个值
Form.CheckForIllegalCrossThreadCalls = false;
}
是不是在txtNum中,显示出了数字在变化。这时候你拖动一下窗体,它是可以拖动的。而且它在计数完成之后你会发现,你会发现txtInfo的值变了。你可能会说,这个没看出有什么异步啊。你在gn.BeginInvoke(null, null);这句的后面加一个this.txtInfo.Text = "我正在计数";你就会发现了,你在计数的时候,程序还可以再去做别的东西。
是不是很有意思,也很简单。