Решение Apple Doc GCD Producer-Consumer неправильно?

В разделе "Миграция с потоками" руководства по программированию Apple Concurrency есть Изменение производителей-потребителей, в котором утверждается, что типичная многоступенчатая реализация переменных переменных mutex + pthread может быть упрощена с использованием GCD.

С очередями отправки вы можете упростить производителя и потребителя реализации в один вызов:

dispatch_async(queue, ^{
  // Process a work item.
});

Когда ваш продюсер должен выполнить работу, все, что ему нужно сделать, это добавить эту работу в очередь и позволить очереди обрабатывать элемент.

Проблема "Продюсер-потребитель" также известна как проблема Bounded-Buffer, но в приведенном выше описании не упоминается буфер, его привязка или потребитель, не говоря уже о блокировании производителя и потребителя, чтобы избежать превышения/под управлением.

Как это может быть правильным решением?

Ответы

Ответ 1

В решении, описанном в этом документе Apple:

  • Нет буфера, потому что буфер не нужен;
  • Загрузка системы - это граница;
  • Потребители - это задачи.

Скажем, у вас есть несколько производителей и потребителей, производители помещают данные в общий буфер, а потребители читают данные из этого общего буфера. Семафор или монитор используются для синхронизации доступа к совместно используемому буферу, и размер буфера фиксируется, чтобы ограничить объем данных, которые производятся в соответствии с потребляемой им скоростью, следовательно, дросселировать производителя.

В разделе Grand Central Dispatch потребители представляют собой задачи, отправленные в очередь. Поскольку задачами являются Objective-C блоки, производитель не нуждается в буфере, чтобы рассказать потребителю о данных, которые он должен обработать: Objective-C блокирует автоматическое захват объектов, которые они ссылаются.

Например:

// Producer implementation
while (…) {
    id dataProducedByTheProducer;

    // Produce data and place it in dataProducedByTheProducer
    dataProducedByTheProducer = …;

    // Dispatch a new consumer task
    dispatch_async(queue, ^{
        // This task, which is an Objective-C block, is a consumer.
        //
        // Do something with dataProducedByTheProducer, which is
        // the data that would otherwise be placed in the shared
        // buffer of a traditional, semaphore-based producer-consumer
        // implementation.
        //
        // Note that an Objective-C block automatically keeps a
        // strong reference to any Objective-C object referenced
        // inside of it, and the block releases said object when
        // the block itself is released.

        NSString *s = [dataProducedByTheProducer …];
    });
}

Производитель может разместить столько потребительских задач, сколько данных, которые он может произвести. Однако это не означает, что GCD будет запускать потребительские задачи с одинаковой скоростью. GCD использует информацию о операционной системе для управления количеством задач, выполняемых в соответствии с текущей загрузкой системы. Сам производитель не дросселируется, и в большинстве случаев он не должен быть из-за внутренней балансировки нагрузки GCD.

Если фактическая потребность в дросселе продюсера, одно решение состоит в том, чтобы иметь мастер, который будет отправлять n заданий производителя, и каждый потребитель уведомляет мастера (через задание, отправленное после того, как потребитель завершил свое задание), что он закончился, и в этом случае мастер отправил бы другую задачу производителя. В качестве альтернативы, потребитель сам по себе может отправить задание производителя.

В частности, отвечая на адреса, которые вы адресули:

Проблема "Продюсер-потребитель" также известна как проблема Bounded-Buffer, но выше не упоминается буфер

Общий буфер не нужен, потому что потребители представляют собой Objective-C блоки, которые автоматически захватывают данные, которые они ссылаются.

его граница

GCD ограничивает количество отправленных задач в соответствии с текущей загрузкой системы.

или потребителя

Потребители - это задачи, отправленные в очереди GCD.

не говоря уже о блокировании производителя и потребителя, чтобы избежать превышения/ниже пробега

Нет необходимости блокировать, поскольку нет общего буфера. Поскольку каждый потребитель является блоком Objective-C, который захватывает полученные данные с помощью механизма захвата контекста блока Objective-C, существует взаимно-однозначное отношение между потребителем и данными.