如何使用异步调用
下面一段文字从别处引用:
BeginInvoke方法用于启动异步调用。它与需要异步执行的方法具有相同的参数。此外,它还有两个可选参数。第一个参数是一个AsyncCallback委托,该委托引用在异步调用完成时要调用的方法。第二个参数是一个用户定义的对象,该对象可向回调方法传递数据。BeginInvoke立即返回,不会等待异步调用完成,被调用的方法将在线程池线程中执行。因此,提交请求的原始线程与执行异步方法的线程池线程是并行执行的。BeginInvoke会返回一个IAsyncResult对象,可以使用该对象来监视异步调用进度,也可将该对象传递给EndInvoke方法,以获取异步执行的方法的返回值。
EndInvoke方法用于检索异步调用的结果。调用BeginInvoke方法后可随时调用EndInvoke方法;如果异步调用尚未完成,EndInvoke方法将一直阻塞调用线程,直到异步调用完成后才允许调用线程执行。EndInvoke方法的参数包括需要异步执行的方法的out和ref参数,以及由BeginInvoke返回的IAsyncResult对象。因此,通过EndInvoke方法可以获得异步调用的方法的所有输出数据,包括返回值、out和ref参数。
AsyncCallback委托表示在异步操作完成时调用的回调方法,其定义如下:
public delegate void AsyncCallback(IAsyncResult ar);
一、下面是一个简单的例子:
public class Test { private int WorkerFunction(string a, string b) { //this is the guy that is supposed to do the long running work Console.WriteLine(a); Console.WriteLine(b); return a.Length + b.Length; } private void MyCallBack(IAsyncResult ar) { var function = ar.AsyncState as Func<string, string, int>; int result = function.EndInvoke(ar); Thread.Sleep(1000); Console.WriteLine("Result is {0}", result); } public void CallMethod() { var function = new Func<string, string, int>(WorkerFunction); IAsyncResult result = function.BeginInvoke("param1", "param2", MyCallBack, function); //function.BeginInvoke("param1", "param2", MyCallBack, function); Console.WriteLine(" Finished begin invode."); } }
CallMethod()方法中首先定义了一个委托function,再用委托实现异步调用:
IAsyncResult result = function.BeginInvoke("param1", "param2", MyCallBack, function);
BeginInvoke方法有四个参数:
1.前两个是传递给被委托的方法WorkerFunciton(string p1, string p2)
2. 第三个参数MyCallBack是一个回调函数,在异步委托方法执行完成之后,会调用该回调方法。
public delegate void MyCallBack(IAsyncResult ar);
IAsyncResult接口定义了四个公开属性,通过它们可以获取异步调用的状态。
— AsyncState:获取用户定义的对象,它限定或包含关于异步操作的信息。
— AsyncWaitHandle:获取用于等待异步操作完成的 WaitHandle。
— CompletedSynchronously:获取异步操作是否同步完成的指示。
— IsCompleted:获取异步操作是否已完成的指示。
3. 第四个参数function 是用于传递数据给回调函数。传递给MyCallBack的对象function存储在参数ar.AsyncState中。在callBack 函数中使用这个传回的对象前需要先将其转换为function的类型。
var function = ar.AsyncState as Func<string, string, int>;
最后再回调函数中使用EndInvoke(ar)方法结束异步线程的使用。
当只需要实现异步调用,而不关心异步调用状态(什么时候结束)时,可以不用给出回调函数,直接用null替代即可,如下:
function.BeginInvoke("param1", "param2", Null, Null);
二、在socket client 程序中,经常需要连接server端,下面是一个异步连接server的实例,包括server端的连接,数据发送:
public class Client { private readonly string _hostname; private readonly int _port; private TcpClient _client; private NetworkStream _networkStream; private const int BufferSize = 4096; private readonly byte[] _buffer = new byte[BufferSize]; public delegate void OnConnectedDelegate(); public delegate void OnDataSentDelegate(string msg); public delegate void OnDataRecvDelegate(string msg); public delegate void OnClosedDelegate(); public OnConnectedDelegate OnConnected; public OnClosedDelegate OnClosed; public OnDataSentDelegate OnDataSent; public OnDataRecvDelegate OnDataRecv; public Client(string hostname, int port) { if (string.IsNullOrEmpty(hostname) || port <= 0) throw new ArgumentException("Invalid IPAddress."); _hostname = hostname; _port = port; } public void Connect() { if (_client == null) { try { _client = new TcpClient(); _client.BeginConnect(_hostname, _port, OnConnectedCallback, null); } catch (Exception ex) { Logger.ErrorWithFormat("Failed to connect to {0}:{1}. {2}", _hostname, _port, ex.Message); Close(); } } } private void OnConnectedCallback(IAsyncResult ar) { try { _client.EndConnect(ar); _networkStream = _client.GetStream(); if (OnConnected != null) OnConnected(); } catch (Exception ex) { Logger.ErrorWithFormat("Failed to connect to {0}:{1}. {2}", _hostname, _port, ex.Message); Close(); } } public void Send(string msg) { if (string.IsNullOrEmpty(msg)) return; byte[] bytes = Encoding.UTF8.GetBytes(msg); Send(bytes); } public void Send(byte[] bytes) { if (null == bytes) return; if (_networkStream != null) { try { var msg = Encoding.UTF8.GetString(bytes); _networkStream.BeginWrite(bytes, 0, bytes.Length, SendCallback, msg); } catch (Exception ex) { Logger.ErrorWithFormat("Failed to send {0} bytes. {1}", bytes.Length, ex.Message); Close(); } } } private void SendCallback(IAsyncResult ar) { if (_networkStream != null) { try { _networkStream.EndWrite(ar); var msg = ar.AsyncState as string; Logger.InfoWithFormat(" >> Sent msg : {0}", msg); if (OnDataSent != null) { OnDataSent(msg); } } catch (Exception ex) { Logger.ErrorWithFormat("Failed to sent to {0}:{1}. {2}", _hostname, _port, ex.Message); Close(); } } } public void Read() { if (_networkStream != null) { try { _networkStream.BeginRead(_buffer, 0, _buffer.Length, ReadCallback, null); } catch (Exception ex) { Logger.ErrorWithFormat("Failed to read message. {0}", ex.Message); Close(); } } } private void ReadCallback(IAsyncResult ar) { if (_networkStream != null) { try { var bytesRead = _networkStream.EndRead(ar); if (bytesRead > 0) { var msg = Encoding.UTF8.GetString(_buffer, 0, bytesRead); Logger.InfoWithFormat(" >> Read msg : {0}", msg); if (OnDataRecv != null) { OnDataRecv(msg); } } } catch (Exception ex) { Logger.ErrorWithFormat("Failed to read to {0}:{1}. {2}", _hostname, _port, ex.Message); Close(); } } } public void Close() { if (_networkStream != null) { _networkStream.Close(); _networkStream = null; } if (_client != null) { _client.Close(); _client = null; } if (OnClosed != null) { OnClosed(); } } }