MorgoZ
MorgoZ

Reputation: 2052

Communication between a thread and a task waiting

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.

Upvotes: 3

Views: 2665

Answers (2)

usr
usr

Reputation: 171178

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.

Upvotes: 2

mtmk
mtmk

Reputation: 6316

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.

Upvotes: 1

Related Questions