I can't understand why this strange behavior when I call the method remotely is expensive.
I have observed a performance issue when my object method called remotely compared to when it's called within the same process. To confess, I don't have good understanding of .NET remoting technology. Yes I have read few articles, but I haven't understood how to resolve this specific problem.
I had supportive data from ants performance profiler which clearly shows call to DisconnectFromPublisher()-> UnsubscribeAll();
is expensive.
I have modified my code to simplify the problem. The skeleton is as follows. Give me some pointers why the method is expensive when it's invoked remotely.
/// <summary>
/// A data object that holds callback information.
/// </summary>
public class CallbackDelegate
{
/// <summary>
/// The delegate that should be invoked to perform the callback.
/// </summary>
public SampleDelegate SampleDelegate;
/// <summary>
/// The client control that has the callback method.
/// This value can be null.
/// </summary>
public Control ClientControl;
/// <summary>
/// a unique key is used to identify each callback info
/// </summary>
public Guid CallbackDelegateKey;
}
/// <summary>
/// A helper class that allows clients to receive notification messages
/// by subscribing to topics.
/// </summary>
public class Subscriber : MarshalByRefObject
{
#if DEBUG
// Allows exceptions to be injected during unit testing of debug builds.
private string _testCase = null;
#endif
/// <summary>
/// The publisher to which this subscriber is connected.
/// </summary>
public Publisher Publisher
{
get { return _publisher; }
}
private Publisher _publisher;
/// <summary>
/// Notification Dispatcher
/// </summary>
private Dispatcher _Dispatcher = new Dispatcher();
private Dictionary<string, List<CallbackDelegate>> _clientCallbacks = new Dictionary<string, List<CallbackDelegate>>();
/// <summary>
/// sync object for client callbacks list
/// </summary>
private object _syncObj = new object();
/// <summary>
/// Constructor for a notification subscriber.
/// </summary>
public Subscriber()
{
_Dispatcher.Notify += new EventHandler<NotificationEventArgs> (_Dispatcher_Notify);
}
public void Unsubscribe(string topicName)
{
// Check the method parameter.
if (topicName == null)
{
throw new ArgumentNullException();
}
try
{
lock (_syncObj)
{
// Remove the client callback (indexed by topic name).
_clientCallbacks.Remove(topicName);
}
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// Unsubscribes from all topics that this object has susbcribed to.
/// </summary>
/// <exception cref="PublisherAccessException">An error occurred while unsubscribing from a topic.</exception>
public void UnsubscribeAll()
{
lock (_syncObj)
{
// Access the topic names before modifying the list (with Unsubscribe).
Queue<string> topics = new Queue<string>();
foreach (string name in _clientCallbacks.Keys)
{
topics.Enqueue(name);
}
//For each topic name...
while (topics.Count > 0)
{
string topicName = topics.Dequeue();
// Unsubscribe the topic.
Unsubscribe(topicName);
}
}
}
/// <summary>
/// Disconnects from the publisher.
/// This subscriber will no longer subscribe to any topics.
/// </summary>
/// <exception cref="PublisherAccessException">An error occurred while unsubscribing from a topic.</exception>
public void DisconnectFromPublisher()
{
try
{
_Dispatcher.Stop();
// Unsubscribe from all topic.
UnsubscribeAll();
}
catch (PublisherAccessException)
{
throw;
}
catch (Exception ex)
{
throw new PublisherAccessException("An error occurred while disconnecting from the notification publisher.", ex);
}
finally
{
// Do not keep a reference to the publisher.
_publisher = null;
lock (_syncObj)
{
_clientCallbacks.Clear();
}
}
}
}
Unsubscribe
andUnsubscribeAll
acquire a lock on_syncObj
so I would guess that is the source. To be honest I'm surprised this doesn't cause a deadlock in the first place since the calls toUnsubscribe
are nested insideUnsubscribeAll
. – Jeroen Vannevel Oct 13 '14 at 9:31lock
is recursive, so if the current thread already owns the lock, it can acquire it again. – svick Oct 13 '14 at 14:00