2

I am looking for a simple and efficient way to asynchronously (wait / block) without having to poll within an Async Task method.

I have created a simple psuedo-code situation below:

private var queue = new ConcurrentQueue<Command>();

        // Function called by program 
        public async Task<String> GetNameAsync(int id)
        {
            //Build Command
            var Command = new Command(id);

            //Enqueue Command to concurrent queue
            queue.enqueue(command);

            //Wait for command to finnish executing, posibly supply a timeout   
            await command.CompleteCondition

            //Once command has completed or timed out return the result of the task.
            return command.Response.getString();
        }


        //Continiously runs in its own thread
        private void MainThread()
        {
            while(mustRun)
            {
                if(queue.Any())
                {
                    //Get command from queue if there is one
                    var command = queue.dequeue();

                    //Execute command
                    var result = ExecuteCcommand(command);

                    //Set Command Done with result (so the blocking async task can continue:
                    //TODO: 

                }else{
                Thread.Sleep(100);          
            }
        }

I have left out the mechanism which I do not know of, But essentially I need to pass some sort of lock along with the command to the main thread which will then notify the Async Task once it has completed, so that the task can continue.

I am sure there must me some type of c# mechanism out there which is specifically designed to be used with the c# Async Task library. I am obviously not aware of it and have never worked with it. What would you recommend I use?

5
  • It's not very clear what you are trying to do here - you queue a command and then immediately execute it. What are you trying to await.? Your code also mentions a blocking async task - what is this about? Commented Sep 8, 2015 at 11:06
  • 1
    It's called TaskCompletionSource - it gives you a Task that you can set as you want (e.g. SetResult/SetException/SetCancelled. Alternatively, you can implement your own awaitable (simply a class/struct that has a method named GetAwaiter :D). Another option would be to turn your "continously running on its own thread" worker into a TaskScheduler, and simply start a task on that scheduler. There's plenty of options, really :) Commented Sep 8, 2015 at 11:07
  • @Paddy The task has to wait for the response. It doesn't help that I just execute the Command and return. I need the response from the command and I need to feed that response back to the awaited task. Commented Sep 8, 2015 at 11:12
  • 1
    It looks like you are trying to implement a Publish/Subscribe mechanism. .NET already has mechanisms to support this without polling or Thread.Sleep, you don't need to implement it from scratch. Check for example ActionBlock<T>. The producer can post to the block and the block's action (the subscriber) will run on a ThreadPool thread without requiring any synchronization Commented Sep 8, 2015 at 11:17
  • As another point, note that if you replace the ConcurrentQueue with a BlockingCollection (which has a queue as the underlying storage by default), you can replace your producer with a fully blocking BlockingCollection.GetConsumingEnumerable - no need for a while (true) or Thread.Sleep. Commented Sep 9, 2015 at 7:48

2 Answers 2

3

The simplest way would be to use a TaskCompletionSource. For example:

public class Command
{
  private readonly TaskCompletionSource<string> _tcs = new TaskCompletionSource<string>();

  public Task<string> ExecuteAsync()
  {
    return _tcs.Task;
  }

  internal void ExecuteCommand()
  {
    if (_tcs.Task.IsCompleted) return;

    try
    {
      // Do your work...

      _tcs.SetResult(result);
    }
    catch (Exception ex) 
    {
      _tcs.SetException(ex);
    }
  }
}

When executing the command, you just var result = await ExecuteAsync();. In your worker, just do ExecuteCommand();.

Sign up to request clarification or add additional context in comments.

5 Comments

i mean how this will work, i did'nt understood it well honestly
@EhsanSajjad It's just a signalling mechanism. It allows you to await an arbitrary event (invoked by calling ExecuteCommand). You need to look at the question to see how it's actually used.
my only issue with this, is that The producer is doing the work. I want to producer to delegate the work to the consumer. and then wait for the consumer to tell it that its done.
@Zapnologica Well... yes? That's how this works. The producer simply awaits while the consumer does its work. It is an explicit signal from the consumer that "wakes" the producer. Note how the ExecuteAsync that the producer is supposed to call doesn't really do anything - it simply returns a task. All the work happens in ExecuteCommand, which will be called by the producer when it gets around to it.
@Luaan oh i see, That makes perfect sense now. Works like a charm. I just wrote a test app and it does exactly what I needed.
1

It seems you are trying to implement a Publish/Subscribe scenario. .NET provides a variety of classes for this.

One of the simplest is ActionBlock class from TPL Dataflow. Producers can post messages (data) to the ActionBlock that gets processed by a consumer Action, on a separate Task. By default, ActionBlock uses a single Task to process messages although this can be changed.

In this case you could write something like:

private ActionBlock<Command> _myBlock=new ActionBlock<Command>(cmd=>ExecuteCommand(cmd));

//In the producer method
_myBlock.Post(command);

ExecuteCommand may be the method you've already implemented. There is no need to handle dequeuing or sleeping, this is handled by the ActionBlock itself.

An issue that isn't addressed in your code, is what to do when you want to stop processing. Ideally you want to stop posting to the queue and wait for any outstanding messages to finish processing. The ActionBlock allows you to simply call Complete() and wait for it to finish by awaiting on its Completion task, eg:

_myBlock.Complete();
await _myBlock.Completion;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.