Многопозиционная потокобезопасная очередь в C

Я работаю над многопоточным приложением C с использованием pthreads. У меня есть один поток, который записывает в базу данных (библиотека базы данных безопасна только для использования в одном потоке), и несколько потоков, которые собирают данные, обрабатывают их, а затем должны отправить результаты в поток базы данных для хранения. Я видел в упомянутом, что "возможно" сделать безопасную очередь с несколькими записями в C, но каждое место, которое я вижу, просто говорит, что это " слишком сложно для этого пример " и просто демонстрирует безопасную очередь с одним писателем.

Мне нужны следующие вещи:

  • эффективные ввод и удаление. Я бы предположил, что, как и любая другая очередь O(1), возможно создание и удаление очереди.
  • динамически выделенная память, т. е. связанная структура. Мне не нужно иметь произвольного ограничения на размер очереди, поэтому массив действительно не то, что я ищу.

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

4 ответов


конечно, есть очереди без блокировки. Основываясь на том, что вы сказали в комментариях, производительность здесь совсем не критична, так как вы все равно создаете поток на запись.

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

write:
    lock the mutex
    (optionally - check the cancel flag to prevent leaks of stuff on the list)
    add the event to the list
    signal the condition variable
    unlock the mutex

read:
   lock the mutex
   while (list is empty AND cancel is false):
       wait on the condition variable with the mutex
   if cancel is false:  // or "if list non-empty", depending on cancel semantics
       remove an event from the list
   unlock the mutex
   return event if we have one, else NULL meaning "cancelled"

cancel:
   lock the mutex
   set the cancel flag
   (optionally - dispose of anything on the list, since the reader will quit)
   signal the condition variable
   unlock the mutex

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

Edit: вы также можете поддерживать несколько читателей (без портативных гарантий, для которых одно получает данное событие), если в cancel вы измените "сигнал" на "широковещательный". Хотя он вам и не нужен, на самом деле он тоже ничего не стоит.


Если вам не нужна свободная от блокировки очередь, вы можете просто обернуть существующую очередь блокировкой.

Mutex myQueueLock;
Queue myQueue; 
void mtQueuePush(int value)
{
    lock(myQueueLock);
    queuePush(myQueue, value);
    unlock(myQueueLock);
}
int mtQueueNext()
{
    lock(myQueueLock);
    int value = queueFront(myQueue);
    queuePop(myQueue);
    unlock(myQueueLock);
    return value;
}

единственное, что после этого, чтобы добавить какой-то handeling для mtQueueNext, когда очередь пуста.

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

Theres nubmer одиночных читателя / писателя lockless очередей вокруг, как большинство из них реализованы в виде классов шаблонов c++. Однако сделайте поиск google и, если нужно, разработайте, как переписать их в простой C.


http://www.liblfds.org

библиотека структуры данных без блокировки, написанная на C.

имеет очередь M&S.


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