1

I'm working on some software that is essentially a pub/sub framework.

On subscription, I have to keep a record of what is being subscribed to, and I need to make both subscription and unsubscription atomic operations. A connection is identified by a guid, and can have multiple subscriptions (Alice wants NEWS and WEATHER, for example). So once Alice establishes a connection with an ID, she can Subscribe() multiple times to events she cares about.

In simple terms, my subscribe looks like this:

ConcurrentDictionary<string, List<string>> _subscriptionsForConnection;
async Task Subscribe()
{
   List<string> subscriptikons = _subscriptionsForConnection.GetOrAdd(connectionId, MakeNewList);
   await underlyingComponentA.SubscribeAsync(xxx);
   subscriptions.Add(xxx);
   await underlyingComponentB.SubscribeAsync(xxx);
}

The problem is, I need to synchronize the dictionary to make sure the Subscribe and Unsubscribe operations don't overlap in a way where the subscriptions list ends up not accurately representing the state of subscriptions.

Conceptually, I would like something along the lines of:

lock(_subscriptionsForconnection)
{
    subscriptions = GetOrAdd();
    await SubscribeA();
    subscriptions.Add();
    await SubscribeB();
}

However .net 4.5 does not allow you to have await inside of a lock for a number of reasons, including that your execution might resume in another context, making it impossible to unlock. (Which means Monitor.Enter/Exit will not work).

Another approach would be to serialize the two underlying component calls and make them run synchronously using the Wait() method, but this seems wrong.

It occurs to me that I'm probably thinking about this problem wrong, so I welcome your suggestions.

Additional Details I've abstracted a few details out of my explanation to make it clearer. For those who are wondering. - I'm developing a component on top of SignalR. underlyingComponentB.SubscribeAsync is actually the Groups.Add call. - underlyingComponentA.SubscribeAsync is an external message bus that will drive my component

Kir
  • 2,905
  • 2
  • 27
  • 44

1 Answers1

1

Use SemaphoreSlim.WaitAsync as a replacement for lock.

usr
  • 168,620
  • 35
  • 240
  • 369
  • Looking at this now: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx So would you suggest for me to create a semaphore object per instance of `_subscriptionsForconnection` and `WaitAsync()` on it instead? – Kir Feb 17 '15 at 20:17
  • I don't know. You wanted lock semantics and this provides them. Hope it helps. – usr Feb 17 '15 at 20:24