Использование BufferBlock с опцией регулирования для потребителя

1

В моем приложении-изготовителе-потребителе я определил BufferBlock как очередь для добавления элементов.

 public static BufferBlock<AppointmentReminder> m_Queue = new BufferBlock<AppointmentReminder>();
 SemaphoreSlim seaphore = new SemaphoreSlim(4);

Затем, чтобы добавить элементы в очередь, у меня есть

    private static void Producer()
    {
        for (int i = 0; i < 5000; i++)
        {
            AppointmentReminder reminder = new AppointmentReminder();
            reminder.UniqueId = Guid.NewGuid();
            reminder.CallMethod = "Number";
            reminder.sString = "1234567890";
            m_Queue.Post(reminder);
        }
        for (int i = 0; i < 3000; i++)
        {
            AppointmentReminder reminder = new AppointmentReminder();
            reminder.UniqueId = Guid.NewGuid();
            reminder.CallMethod = "Letter";
            reminder.sString = "abcdefghij";
            m_Queue.Post(reminder);
        }
        for (int i = 0; i < 2000; i++)
        {
            AppointmentReminder reminder = new AppointmentReminder();
            reminder.UniqueId = Guid.NewGuid();
            reminder.CallMethod = "Mixed";
            reminder.sString = "abcd12345y";
            m_Queue.Post(reminder);
        }
        Console.WriteLine("There are {0} items in the queue.\n", m_Queue.Count);
    }

Теперь мне приходится иметь дело с потребительской частью. Для этого существует метод RunScript(AppointmentReminder callData). Это означает, что нам нужно вызвать метод в потребительской части, если доступно изделие. Но есть ограничение дросселирования. Максимальное количество элементов обработки - 4 в любое время.

Так что я:

    private async static Task Consumer()
    {
        try
        {
            while (await m_Queue.OutputAvailableAsync())
            {
                AppointmentReminder reminder = m_Queue.Receive();
                Call d = new Call();
                d.RunScript(reminder);
            }

        }
        catch (NullReferenceException ex)
        {
            Console.WriteLine("NullReferenceException: " + ex.Message);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

А для гоночного производителя и потребителя,

    static void Main(string[] args)
    {
        AliveEvent = new ManualResetEvent(false);
        Producer(); 
        var consumer = Consumer();
        consumer.Wait();        
    }

Мой вопрос в том, что я не силен в параллельной библиотеке задач (TPL). Как применить ограничение дросселирования к потребителю?

Редактировать: 03 октября 2014:

На основе решения svick. Код для потребителя:

    private async static Task Consumer()
    {
        try
        {
            while (await m_Queue.OutputAvailableAsync())
            {
                var consumerBlock = new ActionBlock<AppointmentReminder>(
remainder => new Call().RunScript(remainder),
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 4 });
                m_Queue.LinkTo(
consumerBlock, new DataflowLinkOptions { PropagateCompletion = true });
                m_Queue.Complete();
                consumerBlock.Completion.Wait();
            }
            // m_Queue is a static BufferBlock in the original code.
        }
  • 0
    Вы приближаетесь к своему редактированию, но все еще не совсем там. Во-первых, while (await m_Queue.OutputAvailableAsync()) теперь полностью избыточен - вы можете от него избавиться. Во-вторых, если ваш потребитель является async Task , вы можете также await consumerBlock.Completion вместо блокировки с помощью Completion.Wait() . В-третьих, m_Queue.Complete() действительно принадлежит производителю (сразу после всех Post ), а не потребителю.
  • 0
    @KirillShlenskiy, я могу удалить m_Queue.Complete() ? Потому что в реальном случае элементы будут добавляться в цикле while. Это означает, что вы всегда будете добавлять элемент в очередь и никогда не останавливаться.
Показать ещё 6 комментариев
Теги:
task-parallel-library
tpl-dataflow

1 ответ

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

Лучшим вариантом, чем самим написанием потребителя, является создание ActionBlock, который уже поддерживает ограничение параллелизма:

var consumerBlock = new ActionBlock<AppointmentReminder>(
    remainder => new Call().RunScript(remainder),
    new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 4 });

И затем свяжите его с очередью:

queue.LinkTo(
    consumerBlock, new DataflowLinkOptions { PropagateCompletion = true });

Наконец, дождитесь его завершения:

queue.Complete();
consumerBlock.Completion.Wait();
  • 0
    Я хочу объединить код вместе. Просто отредактируйте его, не могли бы вы подтвердить это?
  • 0
    @ Люблю нет, это неправильно. Когда вы используете ActionBlock как я и предлагал, вам больше не нужен цикл.
Показать ещё 1 комментарий

Ещё вопросы

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