кольцевой буфер без инверсии приоритета - PullRequest
8 голосов
/ 21 апреля 2011

У меня высокоприоритетный процесс, который должен передавать данные низкоприоритетному процессу. Я написал базовый кольцевой буфер для обработки передачи данных:

class RingBuffer {
  public:
    RingBuffer(int size);
    ~RingBuffer();

    int count() {return (size + end - start) % size;}

    void write(char *data, int bytes) {
      // some work that uses only buffer and end
      end = (end + bytes) % size;
    }

    void read(char *data, int bytes) {
      // some work that uses only buffer and start
      start = (start + bytes) % size;
    }

  private:
    char *buffer;
    const int size;
    int start, end;
};

Вот проблема. Предположим, что у процесса с низким приоритетом есть оракул, который точно сообщает, сколько данных нужно прочитать, так что count() никогда не нужно вызывать. Тогда (если я что-то упустил) нет проблем с параллелизмом. Однако, как только потоку с низким приоритетом необходимо вызвать count() (потоку с высоким приоритетом может потребоваться вызвать его тоже, чтобы проверить, не переполнен ли буфер), есть вероятность, что математика в count () или обновление до конца не является атомарным, что приводит к ошибке.

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

Возможно, я смогу что-то решить, используя атомарные операции, но я не знаю, какая хорошая кроссплатформенная библиотека предоставляет их.

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

Ответы [ 3 ]

4 голосов
/ 21 апреля 2011

Что у вас должно быть в порядке, если вы придерживаетесь следующих рекомендаций:

  • Только один поток может делать записи.
  • Только один поток может читать.
  • Обновления и доступы к start и end являются атомарными.Это может быть автоматическим, например, Microsoft заявляет:

Простые операции чтения и записи в правильно выровненные 32-разрядные переменные являются атомарными операциями.Другими словами, вы не получите только одну обновленную часть переменной;все биты обновляются атомарным способом.

  • Вы допускаете тот факт, что count может быть устаревшим, даже если вы получите значение.В потоке чтения count вернет число минимум , на которое вы можете положиться;для потока записи count вернет максимальное число , а истинное число может быть меньше.
2 голосов
/ 21 апреля 2011

Boost предоставляет кольцевой буфер, но он не безопасен для потоков. К сожалению, я не знаю ни одной реализации.

Предстоящий стандарт C ++ добавляет элементарные операции в стандартную библиотеку, поэтому они будут доступны в будущем, но пока не поддерживаются большинством реализаций.

Я не вижу кроссплатформенного решения, позволяющего сохранять count в здравом уме, пока оба потока пишут в него, если только вы не реализуете блокировку.

Обычно вы, вероятно, используете систему обмена сообщениями и заставляете поток с низким приоритетом запрашивать, чтобы поток с высоким приоритетом делал обновления, или что-то подобное. Например, если поток с низким приоритетом потребляет 15 байтов, он должен попросить поток с высоким приоритетом уменьшить количество на 15.

По сути, вы бы ограничили доступ «запись» к потоку с высоким приоритетом и позволяли только потоку с низким приоритетом читать. Таким образом, вы можете избежать всех блокировок, и потоку с высоким приоритетом не придется беспокоиться о ожидании завершения записи нижним потоком, что делает поток с высоким приоритетом действительно высоким приоритетом.

1 голос
/ 21 апреля 2011

boost::interprocess предлагает кросс-платформенные элементарные функции в boost/interprocess/detail/atomic.hpp

...