В целом сомнительно ли вызывать Task.Factory.StartNew (async () => {}) в разделе «Подписаться»?

1

У меня есть ситуация, когда мне нужно использовать настраиваемый планировщик для запуска задач (это должны быть задачи), и планировщик не устанавливает контекст синхронизации (поэтому я не ObserveOn, SubscribeOn, SynchronizationContextScheduler и т.д.). Вот как я это сделал. Теперь, мне интересно, я не уверен, что это самый подходящий способ делать асинхронные вызовы и ждать их результатов. Это все в порядке или есть более надежный или идиоматический способ?

var orleansScheduler = TaskScheduler.Current;
var someObservable = ...;
someObservable.Subscribe(i =>
{
    Task.Factory.StartNew(async () =>
    {
        return await AsynchronousOperation(i);
    }, CancellationToken.None, TaskCreationOptions.None, orleansScheduler);          
});

Что, если ожидание не понадобится?

<Изменить: Я нашел бетон и упрощенный пример того, что я делаю здесь. В основном я использую Rx в Орлеане, и приведенный выше код - это голые иллюстрации того, что я делаю. Хотя меня тоже интересует эта ситуация в целом.

Окончательный код. Оказывается, это было немного сложно в контексте Орлеана. Я не понимаю, как я могу использовать ObserveOn, и это будет именно то, что я хотел бы использовать. Проблема в том, что, используя это, Subscribe никогда не будет вызвана. Код:

var orleansScheduler = TaskScheduler.Current;
var factory = new TaskFactory(orleansScheduler);
var rxScheduler = new TaskPoolScheduler(factory);
var someObservable = ...;
someObservable
//.ObserveOn(rxScheduler) This doesn't look like useful since...
.SelectMany(i =>
{
    //... we need to set the custom scheduler here explicitly anyway.
    //See Async SelectMany at http://log.paulbetts.org/rx-and-await-some-notes/.
    //Doing the "shorthand" form of .SelectMany(async... would call Task.Run, which
    //in turn runs always on .NET ThreadPool and not on Orleans scheduler and hence
    //the following .Subscribe wouldn't be called. 
    return Task.Factory.StartNew(async () =>
    { 
       //In reality this is an asynchronous grain call. Doing the "shorthand way"
       //(and optionally using ObserveOn) would get the grain called, but not the
       //following .Subscribe. 
       return await AsynchronousOperation(i);
    }, CancellationToken.None, TaskCreationOptions.None, orleansScheduler).Unwrap().ToObservable();
})
.Subscribe(i =>
{
    Trace.WriteLine(i);
});

Также ссылка на связанный поток на форумах Codeplex Orleans.

  • 2
    awaiting in order wouldn't be needed . Не могли бы вы уточнить, что это значит?
  • 3
    Если операция асинхронная, почему вы используете для нее поток пула потоков?
Показать ещё 1 комментарий
Теги:
asynchronous
orleans
system.reactive

2 ответа

4
Лучший ответ

Я настоятельно рекомендую против StartNew для любого современного кода. У него есть прецедент, но он очень редок.

Если вам нужно использовать персонализированный планировщик задач, я рекомендую использовать ObserveOn с TaskPoolScheduler из обертки TaskFactory вокруг вашего планировщика. Это глоток, так вот общая идея:

var factory = new TaskFactory(customScheduler);
var rxScheduler = new TaskPoolScheduler(factory);
someObservable.ObserveOn(rxScheduler)...

Затем вы можете использовать SelectMany для запуска асинхронной операции для каждого события в исходном потоке по мере их поступления.

Альтернативным, менее идеальным решением является использование async void для ваших подписных "событий". Это приемлемо, но вы должны следить за обработкой ошибок. Как правило, не допускайте распространения исключений из метода async void.

Существует третья альтернатива, в которой вы подключаете наблюдаемый в блок потока данных TPL. Блок, такой как ActionBlock может определять свой планировщик задач, а Dataflow естественно понимает асинхронные обработчики. Обратите внимание, что по умолчанию блоки потока данных будут дросселировать обработку на один элемент за раз.

  • 0
    Это выглядит правильно. Я пытался создать планировщик из SynchronizatioContext.Current , но в коде зерна Орлеана он null а потом я не нашел другого способа сделать это. Хотя теперь мне интересно, я мог бы использовать этот rxScheduler в качестве планировщика в Observable.Create вместо или в дополнение к ObserveOn (для справки, используя планировщик ). Я думаю, что было бы интересно обдумать эффекты в контексте Орлеана, но это в другой раз. Я буду на моем компе через ~ 8 часов, мои извинения.
3

Вообще говоря, вместо того, чтобы подписываться на выполнение, лучше/более идиоматично проецировать параметры задачи в выполнение задачи и подписываться только на результаты. Таким образом, вы можете сочинять с дальнейшим Rx вниз по течению.

например, с учетом случайной задачи, например:

static async Task<int> DoubleAsync(int i, Random random)
{
    Console.WriteLine("Started");
    await Task.Delay(TimeSpan.FromSeconds(random.Next(10) + 1));
    return i * 2;
}

Тогда вы можете сделать:

void Main()
{
    var random = new Random();

    // stream of task parameters
    var source = Observable.Range(1, 5);

    // project the task parameters into the task execution, collect and flatten results
    source.SelectMany(i => DoubleAsync(i, random))

          // subscribe just for results, which turn up as they are done
          // gives you flexibility to continue the rx chain here
          .Subscribe(result => Console.WriteLine(result),
                    () => Console.WriteLine("All done."));
}
  • 1
    «Это вообще сомнительно…» Я бы сказал, что это не только сомнительно, но и всегда неправильно . Джеймс имеет правильную идею здесь
  • 0
    Приветствия @PaulBetts. Я думаю, что вам удалось отсечь мой британский резерв там ... :)
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню