Asynchronous Remoting

Asynchronous programming in a remoting scenario is identical to asynchronous programming in a single application domain or context, with the exception of configuration and the requirements of .NET remoting itself. For a complete sample using .NET remoting and synchronous and asynchronous delegates, see Remoting Example: Asynchronous Remoting.

Just like single-application domain asynchronous programming, using asynchronous programming in a .NET remoting scenario means:

  • The caller decides whether a particular remote call is asynchronous.
  • Remote types do not have to explicitly support asynchronous behavior by their clients.
  • The runtime enforces complete type safety.
  • You must use the System.Threading objects appropriately to wait or synchronize your methods.

However, in an application that calls across application-domain or context boundaries, .NET remoting requires you to configure the .NET remoting system and make sure your client programming model also qualifies as a target for a remote call. The reason is fairly simple: if you use asynchronous calls, you might use a callback function that the .NET remoting system will invoke from the server. For example, if you pass a delegate to a static method (which cannot be remoted) or forget to set a port for your channel to "0" (to enable the system to select a client port on your behalf), your call to the server might complete, but you will have no way of obtaining the result because the call back to your client will not complete.

Your client does not need to extend MarshalByRefObject or configure any remote type itself, but otherwise must follow the same rules as any remote type intended to be a server:

  • An instance must receive the callback function.
  • A channel must be registered to listen for the callback function.

Asynchronous Programming Paradigm

The process for asynchronous programming is as straightforward as that for a single application domain:

  1. Create an instance of an object that can receive a remote call to a method.
  2. Wrap that instance method with an AsyncDelegate object.
  3. Wrap the remote method with another delegate.
  4. 5Call BeginInvoke method on the second delegate, passing any arguments, the AsyncDelegate, and some object to hold state (or a null reference — Nothing in Visual Basic).
  5. Wait for the server object to call your callback method.

While this is the general approach, you can vary it to some degree. If you want at any point to wait for a particular call to return, you merely take the IAsyncResult interface that was returned from the BeginInvoke call, retrieve the WaitHandle instance for that object, and call the WaitOne method as shown in the following code example.

Dim RemoteCallback As New AsyncCallback(AddressOf Me.OurCallBack)
Dim RemoteDel As New RemoteAsyncDelegate(AddressOf obj.RemoteMethod)
Dim RemAr As IAsyncResult = RemoteDel.BeginInvoke(RemoteCallback, Nothing)
RemAr.AsyncWaitHandle.WaitOne()
[C#]
AsyncCallback RemoteCallback = new AsyncCallback(this.OurCallBack);
RemoteAsyncDelegate RemoteDel = new RemoteAsyncDelegate(obj.RemoteMethod);
IAsyncResult RemAr = RemoteDel.BeginInvoke(RemoteCallback, null);
RemAr.AsyncWaitHandle.WaitOne();

Alternatively, you can wait either in a loop that checks whether the call has completed or by using System.Threading primitives such as the ManualResetEvent class, and then finish the invocation yourself.

Dim RemoteCallback As New AsyncCallback(AddressOf Me.OurCallBack)
Dim RemoteDel As New RemoteAsyncDelegate(AddressOf obj.RemoteMethod)
Dim RemAr As IAsyncResult = RemoteDel.BeginInvoke(RemoteCallback, Nothing)
If RemAr.IsCompleted Then
  Dim del As RemoteAsyncDelegate = CType(CType(RemAr, AsyncResult).AsyncDelegate, RemoteAsyncDelegate)
  Console.WriteLine(("**SUCCESS**: Result of the remote AsyncCallBack: " _
    + del.EndInvoke(RemAr)))
' Allow the callback thread to interrupt the primary thread to execute the callback.
Thread.Sleep(1)
End If ' Do something.

[C#]
AsyncCallback RemoteCallback = new AsyncCallback(this.OurCallBack);
RemoteAsyncDelegate RemoteDel = new RemoteAsyncDelegate(obj.RemoteMethod);
IAsyncResult RemAr = RemoteDel.BeginInvoke(RemoteCallback, null);
if (RemAr.IsCompleted){
  RemoteAsyncDelegate del = (RemoteAsyncDelegate)((AsyncResult) RemAr).AsyncDelegate;
  Console.WriteLine("**SUCCESS**: Result of the remote AsyncCallBack: "  
    + del.EndInvoke(RemAr) );
// Allow the callback thread to interrupt the primary thread to execute the callback.
Thread.Sleep(1);
}

Finally, you can have your main thread create a ManualResetEvent and wait on the callback function, which then signals the ManualResetEvent as the last line prior to returning. For an example of this type of wait, see the source code comments in Remoting Example: Asynchronous Remoting.

Issues

Note that if the client is a context-bound class that requires a synchronized context, the callback function is dispatched through the .NET remoting context infrastructure. This means the callback function itself can execute asynchronously with respect to its caller for such contexts. This is also the behavior of the OneWayAttribute attribute when decorating callback method signatures. Any such method callback function can execute synchronously or asynchronously with respect to the remote caller, and the caller cannot make any assumptions about completion of such a call when execution control returns to it.

In addition, calling the EndInvoke method before the asynchronous operation is complete a second time with the same IAsyncResult is undefined.

See Also

.NET Remoting Overview | Remoting Example: Asynchronous Remoting | Configuration