在c#异步任务中如何在特定条件下异步等待

在c#异步任务中如何在特定条件下异步等待,c#,multithreading,asynchronous,blocking,C#,Multithreading,Asynchronous,Blocking,我正在寻找一种简单而高效的异步(等待/阻塞)方法,而不必在异步任务方法中轮询 我在下面创建了一个简单的psuedo代码情况: private var queue = new ConcurrentQueue<Command>(); // Function called by program public async Task<String> GetNameAsync(int id) { //Buil

我正在寻找一种简单而高效的异步(等待/阻塞)方法,而不必在
异步任务
方法中轮询

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

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);          
            }
        }
private var queue=new ConcurrentQueue();
//程序调用的函数
公共异步任务GetNameAsync(int id)
{
//生成命令
var命令=新命令(id);
//Enqueue命令到并发队列
queue.enqueue(命令);
//等待命令执行,可能会提供超时
等待命令。完成条件
//命令完成或超时后,返回任务结果。
return命令.Response.getString();
}
//在自己的线程中连续运行
私有void主线程()
{
while(mustRun)
{
if(queue.Any())
{
//从队列中获取命令(如果有)
var命令=queue.dequeue();
//执行命令
var result=executecommand(命令);
//使用结果设置命令完成(以便阻塞异步任务可以继续:
//待办事项:
}否则{
睡眠(100);
}
}
我省略了我不知道的机制,但本质上我需要将某种锁与命令一起传递给主线程,主线程将在异步任务完成后通知它,以便任务可以继续


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

最简单的方法是使用
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);
    }
  }
}
公共类命令
{
私有只读TaskCompletionSource_tcs=new TaskCompletionSource();
公共任务ExecuteAsync()
{
返回_tcs.Task;
}
内部void ExecuteCommand()
{
如果(_tcs.Task.IsCompleted)返回;
尝试
{
//做你的工作。。。
_tcs.SetResult(结果);
}
捕获(例外情况除外)
{
_tcs.SetException(ex);
}
}
}

在执行命令时,您只需执行
var result=await ExecuteAsync();
。在您的工作程序中,只需执行
ExecuteCommand();

似乎您正在尝试实现发布/订阅场景。NET为此提供了多种类

其中最简单的是来自TPL数据流的类。生产者可以在单独的任务中将消息(数据)发布到由使用者操作处理的ActionBlock。默认情况下,ActionBlock使用单个任务来处理消息,尽管这可以更改

在这种情况下,您可以编写如下内容:

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

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

现在还不清楚您想在这里做什么-您将命令排队,然后立即执行它。您想等待什么?您的代码还提到了一个阻塞异步任务-这是关于什么的?它被称为
TaskCompletionSource
-它为您提供了一个
任务
,您可以根据需要进行设置(例如,
SetResult
/
SetException
/
SetCancelled
。或者,您可以实现自己的waitiable(只需一个名为
GetAwaiter
:D的类/结构)。另一个选项是打开“在自己的线程上持续运行”将worker转换为一个
任务调度器
,只需在该调度器上启动一个任务。实际上,有很多选项:)@Paddy任务必须等待响应。我只是执行命令并返回没有帮助。我需要命令的响应,并且需要将该响应反馈给等待的任务。看起来您正在尝试实现发布/订阅机制。NET已经有了无轮询或线程的机制来支持此操作。Sleep,您不需要从头开始实现它。例如,检查。生产者可以发布到块,块的操作(订阅者)将在线程池线程上运行,而不需要任何同步。另一点,请注意,如果您将
ConcurrentQueue
替换为
BlockingCollection
(默认情况下,它有一个队列作为基础存储),您可以用完全阻塞的
BlockingCollection.GetConsumingEnumerable来替换生产者-无需
while(true)
线程。睡眠
。我的意思是这将如何工作,我没有很好地理解它honestly@EhsanSajjad它只是一种信号机制,允许您等待任意事件(通过调用
ExecuteCommand
调用)。你需要看看这个问题,看看它是如何实际使用的。我唯一的问题是,制作人正在做这项工作。我想让制作人把工作委托给消费者。然后等待消费者告诉我已经完成了。@Zapnologica好吧……是吗?这就是工作原理。制作人只是在消费时等待r起作用。它是消费者发出的“唤醒”的明确信号制作人。请注意制作人应该调用的
executeSync
实际上并没有做任何事情-它只是返回一个任务。所有的工作都发生在
ExecuteCommand
中,制作人一有空就会调用它。@Luaan哦,我明白了,这现在很有意义。工作起来很有魅力。我刚刚写了一个测试应用程序,它正是我所需要的。
_myBlock.Complete();
await _myBlock.Completion;