Reputation: 2052
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
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
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