Как написать асинхронный С# 5?

У меня есть следующий сценарий:
Когда команда вводится (для теста это консольное приложение, когда оно готово, я надеюсь, что это будет WebService), я выполняю некоторый код, и когда требуется дальнейший ввод данных, я немедленно возвращаюсь к командному интерпретатору. Когда вводится новый ввод, я хочу, чтобы обработка возобновилась с того места, где я ее оставил. Это звучит так же, как и сценарий асинхронного ожидания С# 5, который я решил попробовать. Я думал об этом:

public void CommandParser()
{
   while(true)
   { 
      string s = Console.ReadLine();
      if (s == "do_something")
         Execute();
      else if (s == "give_parameters")
         SetParameters();
      //... 
   }
}
MySettings input;
public async void Execute()
{
  //do stuff here
  MyResult result = null
  if (/*input needed*/){
     input = new MySetting();
     result = await input.Calculate();
  }
  else { /* fill result synchronously*/}
  //do something with result here

}

public void SetParameters()
{
   if (input!=null)
      input.UseThis("something"); //now it can return from await
}

Теперь мой вопрос: как написать MySettings.Calculate и MySettings.UseThis? Как вернуть задачу из первой и как сигнализировать готовность со второго? Я пытался использовать многие методы factory для задачи, но я не могу найти правильный! Пожалуйста, помогите!

Ответ 1

Один из вариантов заключается в использовании TaskCompletionSource<T>. Это создаст для вас задачу, и вы можете вызвать SetResult или SetException в источнике, что будет соответствующим образом сигнализировать о задаче.

То, что я использовал для реализовать AsyncTaskMethodBuilder<T> для Eduasync - так что вы можете посмотреть на пример.

Вам нужно либо заранее настроить TaskCompletionSource, либо выполнить некоторую другую координацию, чтобы input.Calculate и UseThis знали об одном и том же объекте, но затем Calculate просто вернул completionSource.Task и UseThis будет вызывать completionSource.SetResult.

Имейте в виду, что при вызове SetResult метод async будет продолжать использовать другой поток пула потоков, если вы используете консольное приложение (или веб-сервис), поэтому вы, без сомнения, хотите создать другой TaskCompletionSource для основного цикла, чтобы использовать его для следующего раунда.