C#异步Socket编程

同步与异步:

简单地说,同步即程序执行一个方法,等该方法返回之后,继续往下走,

异步:即程序调用一个方法后立即返回,“宏观”而言,主线程与方法线程并行执行。

就本例而言,socket的异步方法BeginConnect被调用后,接着执行主线程中该语句之后的代码,即:

for (int i = 0; i <5; i++)

Console.WriteLine("Do Some Other Work.");

byte[] byteData = Encoding.ASCII.GetBytes("Some Data.");

如果调用的是同步方法,那么,输出“Socket connected to…”一定会在“Do Some Other Work.”之前,因为主线程必须等待同步方法的返回,但是在异步的情况下,将可能在5行“Do Some Other Work.”之间的某个时刻出现“Socket connected to…”的输出(实际情况还依赖于操作系统的线程调度)。

至于在sClient.BeginSend(…)方法之前调用ConnectDone.WaitOne(),则是由于前者依赖于连接操作(即ConnectCallback)的完成,须得同步一下。

 

(注:ManualResetEvent 允许线程通过发信号互相通信。通常,此通信涉及一个线程在其他线程进行之前必须完成的任务。

ManualResetEvent就像一个信号灯,可以利用它的信号来通知其它线程。它有几个重要的方法:Reset(),Set(),WaitOne()。初始化该对象时,用户可以指定其默认的状态(有信号/无信号),在初始化以后,该对象将保持原来的状态不变直到它的Reset()或者Set()方法被调用,Reset()方法将其设置为无信号状态,Set()方法将其设置为有信号状态。WaitOne()方法使当前线程挂起直到ManualResetEvent对象处于有信号状态,此时该线程将被激活。)

 

本例中,主线程调用ConnectDone.WaitOne();后,主线程阻塞,直到连接操作(即ConnectCallback)完成,因为连接操作完成之后,执行了ConnectDone.Set()将信号灯设置为有信号,由于调用了ManualResetEvent.WaitOne()方法而处在等待状态的主线程将接收到这个信号,于是它接着往下执行,完成后边的工作。

 

http://blog.csdn.net/ismycxp/archive/2007/03/24/1540107.aspx

以SOCKET通信中的异步方法为例:

public static ManualResetEvent ConnectDone = new ManualResetEvent(false); public static void ConnectCallback(IAsyncResult ar) { Socket sClient = (Socket)ar.AsyncState; sClient.EndConnect(ar); Console.WriteLine("Socket connected to {0}", sClient.RemoteEndPoint.ToString()); ConnectDone.Set(); } public static void Main(string[] arg) { try { IPHostEntry ipHost = Dns.Resolve("127.0.0.1"); IPAddress ipAddr = ipHost.AddressList[0]; IPEndPoint endPoint = new IPEndPoint(ipAddr, 11000); Socket sClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); sClient.BeginConnect(endPoint, new AsyncCallback(ConnectCallback), sClient); for (int i = 0; i <5; i++) Console.WriteLine("Do Some Other Work."); byte[] byteData = Encoding.ASCII.GetBytes("Some Data."); ConnectDone.WaitOne(); sClient.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), sClient); ……… }   

 

 

 

C#异步编程模式IAsyncResult概述

IAsyncResult 异步设计模式通过名为 BeginOperationName 和 EndOperationName 的两个方法来实现原同步方法的异步调用,如 FileStream 类提供了 BeginRead 和 EndRead 方法来从文件异步读取字节,它们是 Read 方法的异步版本

Begin 方法包含同步方法签名中的任何参数,此外还包含另外两个参数:一个AsyncCallback 委托和一个用户定义的状态对象。委托用来调用回调方法,状态对象是用来向回调方法传递状态信息。该方法返回一个实现 IAsyncResult 接口的对象

End 方法用于结束异步操作并返回结果,因此包含同步方法签名中的 ref 和 out 参数,返回值类型也与同步方法相同。该方法还包括一个 IAsyncResult 参数,用于获取异步操作是否完成的信息,当然在使用时就必须传入对应的 Begin 方法返回的对象实例

开始异步操作后如果要阻止应用程序,可以直接调用 End 方法,这会阻止应用程序直到异步操作完成后再继续执行。也可以使用 IAsyncResult 的 AsyncWaitHandle 属性,调用其中的WaitOne等方法来阻塞线程。这两种方法的区别不大,只是前者必须一直等待而后者可以设置等待超时

如果不阻止应用程序,则可以通过轮循 IAsyncResult 的 IsCompleted 状态来判断操作是否完成,或使用 AsyncCallback 委托来结束异步操作。AsyncCallback 委托包含一个 IAsyncResult 的签名,回调方法内部再调用 End 方法来获取操作执行结果

C#异步编程模式IAsyncResult之IAsyncResult 接口

 

AsyncCallback为客户端应用程序提供完成异步操作的方法。开始异步操作时,该回调委托被提供给客户端。AsyncCallback 引用的事件处理程序包含完成客户端异步任务的程序逻辑。
AsyncCallback 使用 IAsyncResult 接口获取异步操作的状态。IAsyncResult接口,它表示异步操作的状态.该接口定义了4个公用属性

public interface IAsyncResult//接口 { Object AsyncState { get; }//返回被作为最后一个参数(作为开始操作方法调用的一部分)提供的对象。 WaitHandle AsyncWaitHandle { get; }//AsyncWaitHandle 属性返回 WaitHandle,它可用于执行 WaitHandle.WaitOne、WaitAny 或 WaitAll。 bool CompletedSynchronously { get; }//如果开始操作调用同步完成,则 CompletedSynchronously 属性将被设置为“true”。 bool IsCompleted { get; }//在服务器完成调用处理后,IsCompleted 属性将被设置为“true”。 }

 

AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy)

让我们来看看同步异步的区别:

同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果

异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作

.NET框架基类库中有好几种类都可以提供同步和异步的方法调用。

因为同步方法调用会导致程序流程中途等待,所以采用同步方法的情况下往往会导致程序执行的延迟

相比来说,在某些条件下选择异步方法调用就可能更好一些

例如,有的时候程序需要给多个Web服务发出请求,还有远程处理信道(HTTP、TCP)和代理,这时就最好采用异步方法

.NET Framework允许异步调用任何方法,定义与需要调用的方法具有相同签名的委托

CLR将自动为该委托定义添加适当签名的BeginInvoke虚方法和EndInvoke虚方法和Invoke方法。

关于委托的这3个方法的详细说明可以参考这文章

http://www.cnblogs.com/aierong/archive/2005/05/25/162181.html

我们先来了解这2个方法和一个委托和一个接口:

(1)

BeginInvoke 方法可启动异步调用。

它与您需要异步执行的方法具有相同的参数,另外它还有两个可选参数。第一个参数是一个 AsyncCallback 委托,该委托引用在异步调用完成时要调用的方法。第二个参数是一个用户定义的对象,该对象可向回调方法传递信息。BeginInvoke 立即返回,不等待异步调用完成。BeginInvoke 会返回 IAsyncResult,这个结果可用于监视异步调用进度。

结果对象IAsyncResult是从开始操作返回的,并且可用于获取有关异步开始操作是否已完成的状态。

结果对象被传递到结束操作,该操作返回调用的最终返回值。

在开始操作中可以提供可选的回调。如果提供回调,在调用结束后,将调用该回调;并且回调中的代码可以调用结束操作。

(2)

EndInvoke 方法检索异步调用的结果。

调用 BeginInvoke 后可随时调用 EndInvoke 方法;如果异步调用尚未完成,EndInvoke 将一直阻止调用线程,直到异步调用完成后才允许调用线程执行。EndInvoke 的参数包括您需要异步执行的方法的 out 和 ref 参数以及由 BeginInvoke 返回的 IAsyncResult。

(3)

AsyncCallback委托用于指定在开始操作完成后应被调用的方法

AsyncCallback委托被作为开始操作上的第二个到最后一个参数传递

代码原型如下:

[Serializable]

public delegate void AsyncCallback(IAsyncResult ar);

AsyncCallback为客户端应用程序提供完成异步操作的方法。开始异步操作时,该回调委托被提供给客户端。AsyncCallback 引用的事件处理程序包含完成客户端异步任务的程序逻辑。

AsyncCallback 使用 IAsyncResult 接口获取异步操作的状态。

(4)

IAsyncResult接口

它表示异步操作的状态.

该接口定义了4个公用属性

代码原型如下:

public interface IAsyncResult

实际上,发起和完成.NET异步调用有4种方案可供你选择

1.方案1-使用EndInvoke等待异步调用

异步执行方法最简单的方式是通过调用委托的 BeginInvoke 方法来开始执行方法,在主线程上执行一些工作,然后调用委托的 EndInvoke 方法。EndInvoke 可能会阻止调用线程,因为它直到异步调用完成之后才返回。这种技术非常适合文件或网络操作,但是由于 EndInvoke 会阻止它,所以不要从服务于用户界面的线程中调用它。

还是先来段自己喜欢的控制台代码:

 1AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情using System;

 2AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情

 3AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情namespace ConsoleApplication1

 4AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情{

 5AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情    class Class1

 6AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情    AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情{

 7AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情        public delegate void AsyncEventHandler();

 8AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情

 9AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情        void Event1()

10AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情        AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情{

11AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            Console.WriteLine("Event1 Start");

12AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            System.Threading.Thread.Sleep(2000);

13AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            Console.WriteLine("Event1 End");

14AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情        }

15AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情

16AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情        void Event2()

17AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情        AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情{

18AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            Console.WriteLine("Event2 Start");

19AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            int i=1;

20AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            while(i<1000)

21AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情{

22AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情                i=i+1;

23AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情                Console.WriteLine("Event2 "+i.ToString());

24AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            }

25AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            Console.WriteLine("Event2 End");

26AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情        }

27AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情

28AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情        void CallbackMethod(IAsyncResult ar) 

29AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情        AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情{

30AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            ((AsyncEventHandler) ar.AsyncState).EndInvoke(ar);

31AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情        }

32AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情

33AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情

34AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情        [STAThread]

35AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情        static void Main(string[] args)

36AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情        AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情{

37AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            long start=0;

38AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            long end=0;

39AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            Class1 c = new Class1();

40AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            Console.WriteLine("ready");

41AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            start=DateTime.Now.Ticks;

42AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情

43AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            AsyncEventHandler asy = new AsyncEventHandler(c.Event1);

44AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            IAsyncResult ia=asy.BeginInvoke(null,null);

45AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            c.Event2();

46AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            asy.EndInvoke(ia);

47AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            

48AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            end =DateTime.Now.Ticks;

49AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            Console.WriteLine("时间刻度差="+ Convert.ToString(end-start) );

50AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情            Console.ReadLine();

51AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情        }

52AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情    }

53AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情}

54AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用(copy) - suweidian - 放飞欣情

此程序简单,异步的处理过程在代码43-46这几行

结果如下:

C#异步Socket编程_第1张图片

现在让我们来看看同步处理

修改代码43-46这几行代码:

c.Event1();

c.Event2();

结果如下:

C#异步Socket编程_第2张图片

前者的时间刻度大大小于后者

我们可以明显地看到异步运行的速度优越性

2.方案2-轮询异步调用完成

由 BeginInvoke 返回的IAsyncResult.IsCompleted属性获取异步操作是否已完成的指示,发现异步调用何时完成.

从用户界面的服务线程中进行异步调用时可以执行此操作。轮询完成允许调用线程在异步调用在 ThreadPool 线程上执行时继续执行。

再次修改代码43-46这几行代码:

AsyncEventHandler asy = new AsyncEventHandler(c.Event1);

IAsyncResult ia=asy.BeginInvoke(null,null);

c.Event2();

while(!ia.IsCompleted)

{

}

asy.EndInvoke(ia);

3.方案3-使用WaitHandle等待异步调用

IAsyncResult.AsyncWaitHandle属性获取用于等待异步操作完成的WaitHandle

WaitHandle.WaitOne方法阻塞当前线程,直到当前的WaitHandle收到信号

使用WaitHandle,则在异步调用完成之后,但在通过调用EndInvoke结果之前,可以执行其他处理

再次修改代码43-46这几行代码:

AsyncEventHandler asy = new AsyncEventHandler(c.Event1);

IAsyncResult ia=asy.BeginInvoke(null,null);

c.Event2();

ia.AsyncWaitHandle.WaitOne();

4.方案4-异步调用完成时执行回调方法

如果启动异步调用的线程不需要是处理结果的线程,则可以在调用完成时执行回调方法。回调方法在 ThreadPool 线程上执行。

若要使用回调方法,必须将引用回调方法的 AsyncCallback 委托传递给 BeginInvoke。也可以传递包含回调方法将要使用的信息的对象。例如,可以传递启动调用时曾使用的委托,以便回调方法能够调用 EndInvoke。

再次修改代码43-46这几行代码:

AsyncEventHandler asy = new AsyncEventHandler(c.Event1);

asy.BeginInvoke(new AsyncCallback(c.CallbackMethod),asy);

c.Event2();

 

总结:

四种使用BeginInvoke和EndInvoke进行异步调用的常用方法。在调用BeginInvoke 之后,可以执行下列操作:

1.进行某些操作,然后调用 EndInvoke 一直阻止到调用完成。

2.使用 System.IAsyncResult.AsyncWaitHandle 属性获取 WaitHandle,使用它的 WaitOne 方法一直阻止执行直到发出 WaitHandle 信号,然后调用 EndInvoke。

3.轮询由 BeginInvoke 返回的 IAsyncResult,确定异步调用何时完成,然后调用 EndInvoke。

4.将用于回调方法的委托传递给 BeginInvoke。异步调用完成后,将在 ThreadPool 线程上执行该方法。该回调方法将调用 EndInvoke。

注意:

每次都要调用EndInvoke来完成异步调用。

你可能感兴趣的:(C#网络核心编程,socket,c#,null,interface,任务,.net)