如何在 C# Async Task 中异步等待某个条件

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

我正在寻找一种简单有效的异步方式(等待/阻塞),而无需在 Async Task 方法中进行轮询。

我在下面创建了一个简单的伪代码情况:

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);          
            }
        }

我遗漏了我不知道的机制,但本质上我需要将某种锁与命令一起传递给主线程,主线程将在完成后通知 Async Task , 以便任务可以继续。

我确信一定有某种类型的 c# 机制,它专门设计用于 c# 异步任务库。我显然不知道它,也从未使用过它。你会推荐我使用什么?

最简单的方法是使用 TaskCompletionSource。例如:

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);
    }
  }
}

执行命令时,你只是var result = await ExecuteAsync();。在您的工作人员中,只需执行 ExecuteCommand();.

您似乎正在尝试实施 Publish/Subscribe 方案。 .NET 为此提供了多种 classes。

最简单的一个是来自 TPL Dataflow 的 ActionBlock class。生产者可以 post 将消息(数据)发送到由消费者 Action 在单独的 Task 上处理的 ActionBlock。默认情况下,ActionBlock 使用单个任务来处理消息,但可以更改。

在这种情况下你可以这样写:

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

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

ExecuteCommand 可能是您已经实施的方法。不需要处理出队或休眠,这由 ActionBlock 本身处理。

您的代码中没有解决的一个问题是,当您想停止处理时该怎么做。理想情况下,您希望停止 posting 到队列并等待任何未完成的消息完成处理。 ActionBlock 允许您简单地调用 Complete() 并通过等待其 Completion 任务来等待它完成,例如:

_myBlock.Complete();
await _myBlock.Completion;