在谈到异步的概念时,先要了解几个概念了。
什么是进程?
当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。
而一个进程又是由多个线程所组成的
什么是线程?
线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。
什么是多线程?
多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
了解完进程、线程、多线程后,再来了解同步和异步
同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果,简单来说就是按照方法的执行顺序从上向下一次执行。
异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作。
举个例子:比如张三叫你吃饭,你说有点事需要完成后再去吃饭,而张三等你做完了事之后,再和你一起去吃饭。这就是同步
或者张三叫你吃饭,你说有点事需要完成后再去吃饭,但是张三就只是叫了下你吃饭,他也没有等你回答,就自己独自去吃了。这就可以是异步。
那么在程序中如何声明一个同步,异步呢,其实提到同步异步,就必须熟悉委托,不熟悉的可以看前面的博客,在这里就不多阐述了。
首先声明一个委托和一个普通方法
private delegate void DoSomethingDelegate(string name); Console.WriteLine("****************btnAsync_Click Start {0}***************", Thread.CurrentThread.ManagedThreadId); DoSomethingDelegate method = new DoSomethingDelegate(this.DoSomethingLong); method.Invoke("123");
Console.WriteLine("****************btnAsync_Click End {0}***************", Thread.CurrentThread.ManagedThreadId); private void DoSomethingLong(string name) { Console.WriteLine("****************DoSomethingLong Start {0}***************", Thread.CurrentThread.ManagedThreadId); long lResult = 0; for (int i = 0; i < 10000000; i++) { lResult += i; } Thread.Sleep(2000); Console.WriteLine("****************DoSomethingLong End {0}***************", Thread.CurrentThread.ManagedThreadId);
可以看出程序的运行程序是从上向下执行的,这就是同步。
但是如果是异步调用的情况,就是另外一种情况
method.BeginInvoke("123", null, null);
你会发现异步时,程序不管你调用的方法有没有执行,它直接往下执行,而异步调用的方法开启了新线程,在后面继续执行。
那么上面BeginInvoke中两个null参数代表着什么呢。
简单来说就是一个是IAsyncResult 类型的委托,和一个动态参数(而一般来说最后一个参数一般当做状态参数来使用)。
举个例子
IAsyncResult asyncResult = null; AsyncCallback callback = t => { Console.WriteLine(t.Equals(asyncResult)); Console.WriteLine(t.AsyncState); Console.WriteLine("这里是回调函数 {0}", Thread.CurrentThread.ManagedThreadId); }; asyncResult = method.BeginInvoke("123", callback, "ming");
通过例子也可以得出 t.AsyncState其实和你BeginInvoke的第三个参数是相等的。其实这个例子还可以得出,method.BeginInvoke()得到的返回值是和BeginInvoke()第二个参数是相等的。
但是,假如有个这样的需求,等待异步结束后,主线程还要做事儿,通过前面的例子可以得出,单单使用BeginInvoke()是做不到的,在这里介绍几种方式:
1)IsCompleted属性,他会判断异步是否完成,在这里就可以加一个判断。
int i = 1; while (!asyncResult.IsCompleted) { Console.WriteLine("*****正在计算,已完成{0}%。。。。", 10 * i++); Thread.Sleep(100); }
这样他会等异步结束后跳出循环,再继续执行主线程。但是这样的做法会影响一点效率,因为它不是在异步结束的第一时间就跳出循环,会有一个sleep时间。
2)WaitOne()方法
asyncResult.AsyncWaitHandle.WaitOne();//一直等待 asyncResult.AsyncWaitHandle.WaitOne(-1);//一直等待 asyncResult.AsyncWaitHandle.WaitOne(1000);//等待1000毫秒,超时就不等待了
3)EndInvoke()方法
method.EndInvoke(asyncResult);
其实EndInvoke()除了会等待线程之外,他还可以接受一个返回值输出
假设有一个带string类型返回值的委托,如果直接invoke也可以得到这个返回值
Func<int, string> func1 = i => { DoSomethingLong("btnAsync_Click"); return "二零一七给力"; };
string s = func1.Invoke(123);
但是你需要开启一个新的线程,而用BeginInvoke是不能得到返回值的,因为它只能返回的是IAsyncResult类型。
这个时候就需要EndInvoke,来实现这个需求
string sResult = func1.EndInvoke(asyncResult)
总的来说这三种方式各有各的优势,需要根据实际情况来使用。
未完待续。。。