Многопоточная сигнализация

Я хочу создать многопоточное приложение, которое выполняет следующие действия: Один поток записывается последовательно в циклический буфер. Тогда будет n-число потоков считывателей, которые ждут некоторого сигнала, инициированного потоком записи, чтобы проснуться и прочитать из кругового буфера. Сигнал должен каким-то образом содержать целочисленное значение, представляющее смещение кругового буфера для чтения. Можно ли это сделать в c++? Любая помощь будет оценена по достоинству.

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

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

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

2 ответа

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

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

Со стороны производителя: если очередь не заполнена, push_back - элемент в очередь.

Со стороны потребителя: если очередь не пуста, вытащите элемент из очереди и обработайте ее.

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

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

Редактировать:

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

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

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

Если вы хотите, чтобы несколько потоков сразу обращались к данным, вы можете подумать о создании нескольких очередей или буферов. Возможно, один круговой буфер на пару производителей/потребителей или одну очередь на входной поток... что бы это ни случилось. Трудно сказать без более конкретного примера.

Изменить 2

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


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

Что касается того, почему это лучше: во многом потому, что он позволяет каждой части системы делать свое дело, с минимальным взаимодействием с другими частями системы.

Когда вы описываете систему, продюсер должен знать о внутренних деталях очереди, а также, по сути, все детали потребительских потоков (какие из них пробуждаются в любой момент времени, которые простаивают в любой момент времени, которые планируют выполнить любую конкретную задачу и т.д.),

Дизайн, который я предлагаю свести к минимуму, заставляет продюсера заниматься производством. Его единственное знание остальной системы состоит в одном: как ставить задачи в очередь, как только они их производят.

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

Сама очередь отвечает за всю необходимую синхронизацию потоков. Синхронизация необходима только тогда, когда задача ставится/удаляется из очереди. Сама очередь довольно многоразовая (может использоваться для почти такой ситуации с производителем-потребителем) и сменяется (например, довольно тривиальное переключение между блокировкой и блокировкой).

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

Резюме. Вы предложили сделать все три части системы более сложными. Хуже того, он переплетает троих, поэтому трудно работать с любой из этих частей в изоляции.

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

licensed under cc by-sa 3.0 with attribution.