3

I am in this situation: many clients can send commands to a controller, and the controller must process each command in order, and send back a response to each client. The clients "awaits" for the response asynchronously.

So, by now, when i receive a command from a client, i enqueue the command in a ConcurrentQueue and run a Task where the client waits for a response asynchronously. The controller has a Thread that is always searching for new commands at the ConcurrentQueue, takes the first, process it and should response to the sender (the client), but here is where i get stuck because i don´t know how to make the controller responds EXACTLY to the client that sent the command (the controller has a queue with many commands but doesn´t know who sent them).

My thinking: It would be great to send a message from a Thread directly to a Task with the response object. Is it something possible?

My code:

private ConcurrentQueue<byte[]> sendBuffer;

public async Task<IViewAppMessage> sendCommandAsync(byte[] command)
{                              
     sendBuffer.Enqueue(command);

      return await Task.Run<IViewAppMessage>(() =>
      {
            //Pool for a response
      }           
}

/* Method executed within a Timer worker thread */
private void senderPool(object stateInfo)
{
    try
    {
        //Stop timer
        senderTimer.Change(Timeout.Infinite, Timeout.Infinite);
        //Take command from FIFO
        byte[] commandToSend;
        if(sendBuffer.TryDequeue(out commandToSend))
        {
            //Send command to camera
            cameraSender.Send(commandToSend, commandToSend.Length);                    
            byte[] response = cameraListener.Receive(ref endPoint);

            IViewAppMessage returnMessage = processResponse(response);

            //Notify the response. HOW?????????????  
        }                
    }
    catch 
    {
        //Notify Error
    }
    //In any case, timer restarts
    finally
    {
        try
        {
            //Arrancamos timer
            senderTimer.Change(100, Timeout.Infinite);
        }
        catch 
        { 
            //Fatal error
        }
    }
}

Thanks!

EDIT:

I know i could use a BlockingCollection with all the reponses, so when the sender receives a response, it allocate it at the collection, and then the clients polls for a response, but with that approach i should give each client an ID (or something similar) to check for a response with its ID. That could be a solution, but i wonder if is it possible to directly send a message to the task with the response, since i think something similar would be a better approach since it wouldn´t be neccesary to make the clients poll nor assing them not concurrent IDs.

MorgoZ
  • 2,012
  • 5
  • 27
  • 54
  • which type of client ?? – BRAHIM Kamel Jul 09 '14 at 08:10
  • Any client. It is a library, so it could be referenced by a Windows Form and the client would be the ÜI or by a WCF server and the clients would be the services endpoints... – MorgoZ Jul 09 '14 at 08:19
  • When you enqueue the command, can't you wrap it in some object that stores details of the sender? – Daniel Kelley Jul 09 '14 at 08:22
  • Yes, i clould... What kind of details? I though about assinging an ID to each command before enqueuing it and make the client to wait for that ID´s response in a BlockingCollection with all the responses, but i just wonder if there is a better solution to directly send a message with the response to the client. – MorgoZ Jul 09 '14 at 08:30
  • 1
    Why not just take a callback function pointer - or are these requests sent over the wire? – Rowland Shaw Jul 09 '14 at 08:45

2 Answers2

2

Do not just enqueue the item to be sent. Include data that allows the consumer to notify the producer. For example, you could include a TaskCompletionSource that is set to completed by the consumer at the appropriate point. The producer can await TaskCompletionSource.Task.

usr
  • 168,620
  • 35
  • 240
  • 369
1

You can use a BlockingCollection instead of the queue - classic producer consumer pattern, check the example in MSDN documentation. This would eliminate the use of timer since queue.Take() would block until an item is available in the queue.

mtmk
  • 6,176
  • 27
  • 32
  • Can you elaborate on how that helps? AFAIK `BlockingCollection` wraps `ConcurrentQueue` by default. – Daniel Kelley Jul 09 '14 at 08:20
  • @DanielKelley bc.Take() would block until an item is added from another thread – mtmk Jul 09 '14 at 08:31
  • Ok, but i don´t see how could it help to send a response to the client. Do you mean to use a SendQueue and a ResponseQueue? As i said to @DanielKelley, i would like to know if there is a direct messaging solution, better than making all the clients to pool for a response with its ID at a ResponseQueue. – MorgoZ Jul 09 '14 at 08:41
  • @MorgoZ maybe create a queue per client? Or use a 'router' to invoke the appropriate client. Also depends on which communication pattern you want to use. Example patterns: http://www.rabbitmq.com/getstarted.html – mtmk Jul 09 '14 at 08:45