Общий список, несколько условий, одна или несколько переменных условий?

Считайте, что у вас есть список:

class CLIENTS
{
public:

CLIENTS();
~CLIENTS();

bool addClient();
bool removeClient();
bool getDataFromClientObj(unsigned int id);
bool storeDataInClientObj(unsigned int id);
private:

// vector, that contains all the clients
boost::ptr_vector<CLIENTOBJ> clients;

// mutex for the client-list
boost::mutex mutex;
};

Далее рассмотрим, что getDataFromClientObj () получает общую блокировку (мьютекс, приватная).
В дополнение к этому вы хотите иметь возможность получать данные от клиента через getDataFromClient ().

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

Ну, вот моя проблема:
Пока getDataFromClient (); ждет (так как это список с несколькими читателями / одним писателем), я не могу добавить новых клиентов или удалить клиента, потому что getDataFromClient () удерживает блокировку мьютекса.

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

Итак, здесь еще раз факты:

  • Threadsafe List (несколько читателей / один писатель)
  • Возможность добавления клиента / удаления клиента в любое время
  • Возможность ожидания (определенного) условия для каждого клиента отдельно (один клиент мог хранить новые данные в своей собственной очереди, а другой — нет; тогда getDataFromClient () должен ждать, пока новые данные не будут доступны для чтения)

Я думаю, что проблема заключается в том, что, учитывая, что для каждого клиента существует одно условие (псевдокод: если (clientsqueue.isEmpty () -> wait) должно быть несколько условных переменных (я ошибаюсь?)

Дальнейшая информация:
ОС: Windows 7
Язык: C ++
Компилятор VS2010

4

Решение

Ваша настройка очень DBish. у вас есть таблица, представленная Clients класс, и несколько экземпляров CLIENTOBJ каждый действует как ряд таблицы, с id действуя в качестве первичного ключа. Однако, насколько я понимаю, каждый клиент на самом деле является очередью данных.

Модель, используемую базами данных, может быть грубо описана как делегирование любого доступа к данным выделенному действию (потоку или процессу) в БД и отправка команд в него с использованием SQL. Проблемы синхронизации решаются с помощью транзакций и предложений SQL (обновление может совершенно не повлиять ни на одну строку, если id не существует, но эта команда не потерпит неудачу, она просто вернет 0 обновленную строку). Возможно, в вашем случае будет интересна аналогичная модель: всего один глобальный мьютекс для представления транзакции, и каждый поток блокирует всю структуру данных, управляет ею и разблокирует. Однако это может быть не очень эффективно.

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

В пределах Clients Например, любой вызов метода превращается в future и promise, Обещание помещается в очередь обещаний, и вызывающие потоки либо возвращают будущее из вызова метода, либо немедленно ожидают его.

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

  • создать нового клиента
  • удалить клиента
  • Если это хранилище, поток БД проверяет, ожидает ли какое-либо чтение и удовлетворяет его, или просто помещает эти данные в очередь клиента.
  • Если это чтение и есть данные, извлеките их из клиентской очереди и передайте их потоку или перенесите в ожидающую очередь чтения клиента, чтобы они были удовлетворены позже, когда данные станут доступны.

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

Вы также можете посвятить одну тему CLIENTOBJ, Затем поток БД становится потоком сортировки, который просто распределяет обещания каждому клиенту. Каждый клиент владеет ожидающей очередью чтения и данных данного idТаким образом, при обработке обещаний нет блокировки.

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

Обновить:

В моем ответе изначально предлагалось следующее:

Другими словами, вы могли бы заменить механизм будущее / обещание простой условной переменной, связанной с каждым потоком, не связанным с БД (будущее и обещание, вероятно, реализованы с использованием переменной cond., Но здесь вы бы сохранили накладные расходы на создание и уничтожение).

Но это делает некоторые неявные предположения о том, как CLIENTS объект используется. Самая безопасная дорога действительно std::future один.

1

Другие решения

Других решений пока нет …