Search code examples
c#multithreadingasynchronousblocking

How to asynchronously wait on a certain condition in c# Async Task


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?


Solution

  • 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();.