如果你還不知道什麼是非同步也不要緊,我們還是來看執行個體,通過執行個體來理解才是最深刻的。
在Remoting中,我們可以使用以下幾種非同步方式:
1、普通非同步
2、回調非同步
3、單向非同步
一個一個來說,首先我們這麼修改我們的遠程對象:
public int ALongTimeMethod(int a,int b,int time)
{
Console.WriteLine("非同步方法呼叫開始");
System.Threading.Thread.Sleep(time);
Console.WriteLine("非同步方法呼叫結束");
return a+b;
}
這個方法傳入2個參數,返回2個參數和表示方法執行成功,方法需要time毫秒的執行時間,這是一個長時間的方法。
如果方法我們通過非同步遠程調用,這裡需要注意到這個方法輸出的行是在伺服器端輸出的而不是用戶端。因此,為了測試簡單,我們還是在採用本機物件,在實現非同步前我們先來看看同步的調用方法,為什麼說這是一種阻塞?因為我們調用了方法主線程就在等待了,看看測試:
DateTime dt=DateTime.Now;
RemoteObject.MyObject app=new RemoteObject.MyObject();
Console.WriteLine(app.ALongTimeMethod(1,2,1000));
Method();
Console.WriteLine("用了"+((TimeSpan)(DateTime.Now-dt)).TotalSeconds+"秒");
Console.ReadLine();
假設method方法是主線程的方法,需要3秒的時間:
private static void Method()
{
Console.WriteLine("主線程方法開始");
System.Threading.Thread.Sleep(3000);
Console.WriteLine("主線程方法結束");
}
好了,現在開始運行程式:
用了4秒,說明在我們的方法開始以後本地就一直在等待了,總共用去的時間=本地方法+遠程方法,對於長時間方法調用這顯然不科學!我們需要改進:
1、普通非同步:
首先在main方法前面加上委託,簽名和傳回型別和非同步方法呼叫一致。
private delegate int MyDelegate(int a,int b,int time);
main方法裡面這麼寫:
DateTime dt=DateTime.Now;
RemoteObject.MyObject app=new RemoteObject.MyObject();
MyDelegate md=new MyDelegate(app.ALongTimeMethod);
IAsyncResult Iar=md.BeginInvoke(1,2,1000,null,null);
Method();
if(!Iar.IsCompleted)
{
Iar.AsyncWaitHandle.WaitOne();
}
else
{
Console.WriteLine("結果是"+md.EndInvoke(Iar));
}
Console.WriteLine("用了"+((TimeSpan)(DateTime.Now-dt)).TotalSeconds+"秒");
Console.ReadLine();
來看一下執行結果:
現在總共執行時間接近於主線程的執行時間了,等於是調用方法基本不佔用時間。
分析一下代碼:Iar.AsyncWaitHandle.WaitOne(); 是阻塞等待非同步方法呼叫完成,在這裡這段代碼是不會被執行的,因為主方法完成的時候,非同步方法呼叫早就IsCompleted了,如果我們修改一下代碼:IAsyncResult Iar=md.BeginInvoke(1,2,5000,null,null);
可以看到,主線程方法結束後就在等待非同步方法呼叫完成了,總共用去了接近於非同步方法呼叫的時間:5秒。
在實際的運用中,主線程往往需要得到非同步方法呼叫的結果,也就是接近於上述的情況,我們在主線程做了少量時間的工作以後最終要是要WaitOne去等待非同步作業返回的結果,才能繼續主線程操作。看第二個圖可以發現,非同步作業僅僅用了1秒,但是要等待3秒的主線程方法完成後再返回結果,這還是不科學啊。因此,我們要使用委託回調的非同步技術。
2、回調非同步:
class MyClient
{
private delegate int MyDelegate(int a,int b,int time);
private static MyDelegate md;
[STAThread]
static void Main(string[] args)
{
DateTime dt=DateTime.Now;
//RemoteObject.MyObject app=(RemoteObject.MyObject)Activator.GetObject(typeof(RemoteObject.MyObject),System.Configuration.ConfigurationSettings.AppSettings["ServiceURL"]);
RemoteObject.MyObject app=new RemoteObject.MyObject();
md=new MyDelegate(app.ALongTimeMethod);
AsyncCallback ac=new AsyncCallback(MyClient.CallBack);
IAsyncResult Iar=md.BeginInvoke(1,2,1000,ac,null);
Method();
Console.WriteLine("用了"+((TimeSpan)(DateTime.Now-dt)).TotalSeconds+"秒");
Console.ReadLine();
}
public static void CallBack(IAsyncResult Iar)
{
if(Iar.IsCompleted)
{
Console.WriteLine("結果是"+md.EndInvoke(Iar));
}
}
private static void Method()
{
Console.WriteLine("主線程方法開始");
System.Threading.Thread.Sleep(3000);
Console.WriteLine("主線程方法結束");
}
}
可以看到我上面的注釋行,去掉遠程調用的注釋,對下面的本地調用注釋,編譯後啟動服務端,再啟動用戶端就是遠程調用了。
非同步呼叫結束,立即就能顯示出結果,如果開啟遠程方法的話,可以看的更加清晰:
用戶端:主線程方法開始-》服務端:非同步方法呼叫開始-》服務端:非同步方法呼叫結束-》用戶端:結果是3-》用戶端:主線程方法結束-》用戶端:用了3.03125秒。
3、單向非同步就是像同步調用方法那樣調用方法,方法卻是非同步完成的,但是不能獲得方法的傳回值而且不能像同步方法那樣取得所調用方法的異常資訊!對於不需要返回資訊的長時間方法,我們可以放手讓它去幹就行了:
遠程對象:
using System;
using System.Runtime.Remoting.Messaging;
namespace RemoteObject
{
public class MyObject:MarshalByRefObject
{
[OneWay]
public void ALongTimeMethodOneWay(int time)
{
Console.WriteLine("非同步方法呼叫開始");
System.Threading.Thread.Sleep(time);
Console.WriteLine("非同步方法呼叫結束");
}
}
}
[OneWay]屬性是Remoting.Messaging的一部分,別忘記using,下面看看用戶端代碼:
using System;
namespace RemoteClient
{
class MyClient
{
[STAThread]
static void Main(string[] args)
{
DateTime dt=DateTime.Now;
RemoteObject.MyObject app=(RemoteObject.MyObject)Activator.GetObject(typeof(RemoteObject.MyObject),System.Configuration.ConfigurationSettings.AppSettings["ServiceURL"]);
//RemoteObject.MyObject app=new RemoteObject.MyObject();
app.ALongTimeMethodOneWay(1000);
Method();
Console.WriteLine("用了"+((TimeSpan)(DateTime.Now-dt)).TotalSeconds+"秒");
Console.ReadLine();
}
private static void Method()
{
Console.WriteLine("主線程方法開始");
System.Threading.Thread.Sleep(3000);
Console.WriteLine("主線程方法結束");
}
}
}
這次我們僅僅只能在遠端偵錯,我們先讓非同步方法呼叫去做,然後就放心的做主線程的事情,其他不管了。
運行結果我描述一下:
用戶端:主線程方法開始-》服務端:非同步方法呼叫開始-》服務端:非同步方法呼叫結束-》用戶端:主線程方法結束-》用戶端:用了3.8秒。
上面說的三種方法,只是非同步編程的一部分,具體怎麼非同步呼叫遠程方法要結合實際的例子,看是否需要用到方法的返回和主線程方法的已耗用時間與遠程方法已耗用時間等結合起來考慮,比如上述的WaitHandle也可以用輪詢來實現:
while(Iar.IsCompleted==false) System.Threading.Thread.Sleep(10);總的來說遠程對象的非同步作業和本機物件的非同步作業是非常接近。