Агент / MailboxProcessor в C #, использующий новый async / await - PullRequest
7 голосов
/ 02 ноября 2010

Этот вопрос объединяет две темы, которые я не до конца понимаю

Читая статью об асинхронности в F #, я натолкнулся на тему Агенты / MailboxProcessors, которые можно использовать для реализации машин реагирующего состояния. Может ли новая функциональность async / await в C # 5 быть использована для реализации чего-то подобного в C #, или уже есть что-то аналог, который был бы более подходящим?

Ответы [ 3 ]

11 голосов
/ 02 ноября 2010

С помощью довольно ужасного взлома вы можете использовать тип MailboxProcessor из C #, используя async.Некоторые трудности заключаются в том, что тип использует некоторые специфичные для F # функции (необязательные аргументы - это опции, функции имеют тип FSharpFunc и т.это уже работает.Это означает, что для построения асинхронного F # из C # вам нужно написать метод, который принимает unt -> Task<T> и создает Async<T>.Я написал пост в блоге , в котором обсуждается разница .

Кстати, если вы хотите поэкспериментировать, вот код, который вы можете использовать:

static FSharpAsync<T> CreateAsync<T>(Func<Task<T>> f)
{ 
  return FSharpAsync.FromContinuations<T>(
    FuncConvert.ToFSharpFunc<
      Tuple< FSharpFunc<T, Unit>, 
             FSharpFunc<Exception, Unit>,
             FSharpFunc<OperationCanceledException, Unit> >>(conts => {
    f().ContinueWith(task => {
      try { conts.Item1.Invoke(task.Result); }
      catch (Exception e) { conts.Item2.Invoke(e); }
    });
  }));
}

static void MailboxProcessor() {
  var body = FuncConvert.ToFSharpFunc<
                FSharpMailboxProcessor<int>, 
                FSharpAsync<Unit>>(mbox =>
    CreateAsync<Unit>(async () => {
      while (true) {
        var msg = await FSharpAsync.StartAsTask
          ( mbox.Receive(FSharpOption<int>.None), 
            FSharpOption<TaskCreationOptions>.None, 
            FSharpOption<CancellationToken>.None );
        Console.WriteLine(msg);
      }
      return null;
    }));
  var agent = FSharpMailboxProcessor<int>.Start(body,
                FSharpOption<CancellationToken>.None);
  agent.Post(1);
  agent.Post(2);
  agent.Post(3);
  Console.ReadLine();
}

Как вы можетевидите, это выглядит действительно ужасно :-).

  • В принципе, можно написать C-дружественную оболочку для типа MailboxProcessor (просто извлеките уродливые биты из этого кода), но есть некоторые проблемы.

  • В F # вы часто используете хвостовую рекурсивную асинхронность для реализации конечного автомата в процессоре почтовых ящиков.Если вы напишите то же самое в C #, вы в конечном итоге получите StackOverflow, поэтому вам нужно будет писать циклы с изменяемым состоянием.

  • Совершенно возможно написать агентв F # и вызвать его из C #.Это всего лишь вопрос демонстрации C # -дружественного интерфейса из F # (используя метод Async.StartAsTask).

3 голосов
/ 02 ноября 2010

В принципе, я ожидаю, что было бы просто перевести эти F # API в C # -plus-async-await.

На практике мне неясно, получится ли это красиво, или уродливо и полно лишних аннотаций типа, или просто не идиоматично и нуждается в некотором API-массаже, чтобы он чувствовал себя как дома в C #. Я думаю, что присяжных нет, пока кто-то не сделает работу и не попробует ее. (Я предполагаю, что в ожидаемой CTP такой выборки нет.)

0 голосов
/ 03 апреля 2012

Вы можете взглянуть на Stact .Через некоторое время он не обновлялся, но если вы хотите сделать что-то с немного лучшей поддержкой C #, вы можете найти это хорошей отправной точкой.Я не думаю, что это актуально с async / await.

...