Как достигнуть эквивалентного использования TPL/TAP?

Ниже у меня есть довольно простое приложение пульта.NET, поглощающее Голубую Очередь Сервисной шины.

Как вы будете видеть, я использую Задачу. Фабрика, чтобы разжечь 25 задач приемника, которые тогда называют мой APM-стиль методом BeginMessageReceive. Затем в конце EndMessageReceive я называю BeginMessageReceive снова, чтобы держать движение петли.

Мой вопрос состоит в том, как я мог достигнуть того же самого вида вещи, но переключающийся с APM-стиля BeginMessageReceive/EndMessageReceive к подходу TPL/TAP, используя рекурсивные Задачи и возможно использовав C# 5.0 async/await?

using System;
using System.Configuration;
using System.Threading.Tasks;
using Microsoft.ServiceBus.Messaging;


namespace ServiceBusConsumer
{
    class Program
    {
        private static QueueClient _queueClient;

        private static void Main(string[] args)
        {    
            var connectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
            _queueClient = QueueClient.CreateFromConnectionString(connectionString, "MyQueue");

            for (var i = 0; i < 25; i++ )
            {
                Task.Factory.StartNew(BeginMessageReceive);
            }

            Console.WriteLine("Waiting for messages...");
            Console.ReadKey();

            _queueClient.Close();

        } //end private static void Main(string[] args)

        private static void BeginMessageReceive()
        {
            _queueClient.BeginReceive(TimeSpan.FromMinutes(5), EndMessageReceive, null);
        }

        private static void EndMessageReceive(IAsyncResult iar)
        {
            var message = _queueClient.EndReceive(iar);
            try
            {
                if (message != null)
                {
                    var msg = message.GetBody();
                    Console.WriteLine("Message: " + msg);

                    if (_queueClient.Mode == ReceiveMode.PeekLock)
                    {
                       //Mark brokered message as completed at which point it's removed from the queue.
                        message.Complete();
                    }
                }
            }
            catch (Exception ex)
            {
                if (_queueClient.Mode == ReceiveMode.PeekLock)
                {
                   //unlock the message and make it available 
                    message.Abandon();
                }

                Console.WriteLine("Exception was thrown: " + ex.GetBaseException().Message);
            }
            finally
            {
                if (message != null)
                {
                    message.Dispose();
                }
            }
            BeginMessageReceive();
        }

    }
}

Новый измененный код для рекурсивно называет себя снова, если перерыв MessageReceive истекает:

private static async Task MessageReceiveAsync()
{
    while (true)
    {
        using (var message = await _queueClient.ReceiveAsync(TimeSpan.FromMinutes(5)))try
        {
            if (message != null)
            {               
                try
                {

                    var msg = message.GetBody();
                    Console.WriteLine("Message: " + msg);

                    if (_queueClient.Mode == ReceiveMode.PeekLock)
                    {
                       //Mark brokered message as completed at which point it's removed from the queue.
                        await message.CompleteAsync();
                    }
                }
                catch (Exception ex)
                {
                    message.AbandonAsync();
                    Console.WriteLine("Exception was thrown: " + ex.GetBaseException().Message);
                }
            }
        }
    }
}
0
nl ja de
Измененный код выше, кажется, добивается цели. Все еще удивление, если PLINQ был бы лучше/легче для этого, если MessageReceiveAsync() собирается выполнить ввод/вывод (WebRequest) операции. По существу, I' m пытающийся найти что-либо подобное сообщению получают операцию от очереди и затем I' ll должен найти что-либо подобное работе для каждого сообщения (который эффективно будет Почтой HTTP);
добавлено автор anderly, источник
К вашему сведению одна из причин я должен управлять сколько MessageReceiveAsync() операции I' m увольнение то, потому что у Голубой очереди Сервисной шины есть ограничение на количество параллельных связей.
добавлено автор anderly, источник

1 ответы

Похоже, что Голубой клиент освобождает, все еще не были обновлены с ПЧЕЛОЙ СИГНАЛА. Не уверенный, что ограбление там...

Anyway, you can create your own APM->TAP wrappers using TaskFactory.FromAsync, as such:

public static class MyAzureExtensions
{
  public static Task ReceiveAsync(this QueueClient @this,
      TimeSpan serverWaitTime)
  {
    return Task.Factory.FromAsync(
        @this.BeginReceive, @this.EndReceive, serverWaitTime, null);
  }

  public static Task CompleteAsync(this BrokeredMessage @this)
  {
    return Task.Factory.FromAsync(@this.BeginComplete, @this.EndComplete, null);
  }
}

Как только вы обернули Голубые требования в ГОТОВЫЙ К СИГНАЛУ API, можно использовать их как таковой:

private static void Main(string[] args)
{    
  var connectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
  _queueClient = QueueClient.CreateFromConnectionString(connectionString, "MyQueue");

  for (var i = 0; i < 25; i++ )
    MyMessageReceiveAsync();

  Console.WriteLine("Waiting for messages...");
  Console.ReadKey();

  _queueClient.Close();
}

private static async Task MyMessageReceiveAsync()
{
  while (true)
  {
    using (var message = await _queueClient.ReceiveAsync(TimeSpan.FromMinutes(5)))
    {
      try
      {
        var msg = message.GetBody();
        Console.WriteLine("Message: " + msg);

        if (_queueClient.Mode == ReceiveMode.PeekLock)
        {
         //Mark brokered message as completed at which point it's removed from the queue.
          await message.CompleteAsync();
        }
      }
      catch (Exception ex)
      {
        if (_queueClient.Mode == ReceiveMode.PeekLock)
        {
         //unlock the message and make it available 
          message.Abandon();
        }

        Console.WriteLine("Exception was thrown: " + ex.GetBaseException().Message);
      }
    }
  }
}

Одна выгода для использования async как это - то, что вы не связываете нити пула потоков излишне. Оригинальные используемые 25 нитей, чтобы послушать; мой образец не будет использовать нитей, чтобы послушать. Единственное время нить пула потоков занята в моем образце, - когда сообщение оставляется (в отделении обработки ошибок).

Там одно главное семантическое различие от оригинального кода: если бы QueueClient "получают", поднимает исключение, в оригинальном коде, это разбило бы процесс; в моем примере было бы проигнорировано исключение.

4
добавлено
Потрясающий! That' s точно, чего я хотел достигнуть. Огромное спасибо Стивен!
добавлено автор anderly, источник
Теперь, только одна проблема I' m столкновение и не уверенный, как решить. Как вы видите выше в звонке в ReceiveAsync, я определяю, что сервер ждет перерыв 5 минут. Так, после 5 минут не получения сообщений, моего приложения пульта won' t получают дальнейшие сообщения. Я знаю, что можно установить это до 24 дней, но какая-либо идея о том, как изменить это, чтобы составлять это и всегда держать 25 "слушателей", идущих? В конечном счете это будет помещено в Сервисную Роль Рабочего / Голубую Роль Рабочего, таким образом, мне будет нужна она, чтобы работать на основе непрерывного цикла.
добавлено автор anderly, источник
Нет, приложение пульта ServiceBusConsumer просто сидит, там бегая. И затем если я стою в очереди новые сообщения (в моем другом приложении пульта, что очереди проверяют сообщения), они don' t взят, если я не перезапускаю приложение пульта ServiceBusConsumer. Возможно, it' s бросок исключения в ReceiveAsync, но поскольку вы указали, это могло бы становиться глотавшим и так я don' t видят его.
добавлено автор anderly, источник
I' ve, сделанный что-то подобное в прошлом с обычаем класс ThreadPool, который имеет очередь производителя/потребителя и использует Монитор. Ждать/Контролировать. Пульс, чтобы не вращаться и that' s по существу, что I' m пытающийся сделать использование TPL/TAP. Я хочу всегда иметь 25 "рабочих", ждущих (не вращающийся) и пытающийся получить сообщения асинхронно от очереди.
добавлено автор anderly, источник
Хорошо, внесенный небольшое изменение (см. новый код ниже моего оригинального поста выше), который, кажется, составляет сообщение, ждут перерыв (в этом случае, сообщение пустое), и затем просто рекурсивно называет себя снова, который по существу начался бы, новый async получают метод (эффективно перезапускающий перерыв ожидания). Сообщите мне то, что вы думаете.
добавлено автор anderly, источник
Технически, это похоже с Рекурсивным вызовом MessageReceiveAsync, в то время как (истинная) петля больше не необходима. Хотя, I' m не уверенный, почему это wasn' t работающий, так как это должно быть перекручивание, навсегда пытаясь получить от очереди.
добавлено автор anderly, источник
Положительная сторона, я изменил исходную версию (см. выше) так, чтобы это составляло сообщение, являющееся пустым, если перерыв ожидания истекает, но избегает рекурсивного вызова MessageReceiveAsync. Походит на обработку сообщения ==, пустой указатель был ключом в любом сценарии.
добавлено автор anderly, источник
Что происходит на перерыве? Это поднимает исключение?
добавлено автор Stephen Cleary, источник
Если это бросает исключение, то можно поймать его. Просто переместите использование в попытка .
добавлено автор Stephen Cleary, источник
Я обычно избегаю значительной рекурсии, и определенно избегаю бесконечной рекурсии. C# doesn' t гарантируют оптимизацию последнего вызова - и they' ре, не планируя добавить его, также.
добавлено автор Stephen Cleary, источник
Microsoft Stack Jobs
Microsoft Stack Jobs
1 788 участник(ов)

Work & freelance only Microsoft Stack. Feed https://t.me/Microsoftstackjobsfeed Чат про F#: @Fsharp_chat Чат про C#: @CSharpChat Чат про Xamarin: @xamarin_russia Чат общения:@dotnettalks

Microsoft Developer Community Chat
Microsoft Developer Community Chat
584 участник(ов)

Чат для разработчиков и системных администраторов Microsoft Developer Community. __________ Новостной канал: @msdevru __________ Баним за: оскорбления, мат, рекламу, флуд, флейм, спам, NSFW контент, а также большое количество оффтоп тем. @banofbot