Semaphores and Web Sockets

1.2k views Asked by At

I am trying to understand and fix the exception I am receiving:

There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can be called simultaneously, but at most one outstanding operation for each of them is allowed at the same time.

So I have multiple threads going to a handler which wants to send specific clients specific information.

When a client connects a mapping is created from that client's specific connection to the data that he or she wants to have streamed to them via a web socket connection.

My code looks like:

foreach (KeyValuePair<socketInfo, data> pair in mapping)
        {
            //Get the unique sendAsync per connection
            WebSocketSendAsync sendAsync = pair.Key.sendAsync;

            //Get the data the specific connection wnats
            dynamic info = fillData(pair.Value);

            //Convert the data to send to bytes
            string sendInfo = Newtonsoft.Json.JsonConvert.SerializeObject(attributeInfo);
            byte[] infoBytes = System.Text.Encoding.UTF8.GetBytes(sendInfo);

            //Send the data
            Semaphore send = new Semaphore(1, 1);
            send.WaitOne();
            await sendAsync(new ArraySegment<byte>(infoBytes), 1, false, pair.Key.callCancelled);
            send.Release(1);

        }

I understand their can be only one sendAsync going at a time (even if multiple threads are trying to do it?), so I figured a semaphore would be the proper way to go about this. I want only one thread to be able to use the await sendAsync method at a time and have the other threads wait till the previous one is finished.

This is my first time using semaphores so I am not sure why it is not working, any help?

1

There are 1 answers

0
Absolom On BEST ANSWER

The problem seems that your instance of Semaphore is created on each loop. It should instead be created once and then you can use this instance to protect your code from being accessed by more than one thread at the same time.

I suggest that you use the SemaphoreSlim instead of Semaphore since you are using async/await in your code. This class has a WaitAsync method which is an awaitable method.

public class MyClass
{
   SempahoreSlim _semaphore = new SemaphoreSlim(1, 1);

   public void Foo(/*...*/)
   {
       foreach(/*...*/)
       {
            /*...*/

            await _semaphore.WaitAsync();

            try
            {
                await sendAsync(/*...*/);
            }
            finally
            {
                _semaphore.Release();
            }
       }
   }
}