Считайте, что у вас есть список:
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 () удерживает блокировку мьютекса.
Как бы вы точно решили сценарий, если у вас есть список, который должен быть потокобезопасным + ожидание переменной условия для конкретного клиента + при ожидании клиента, иметь возможность удалить или добавить любого из клиентов, удерживаемых в списке?
Итак, здесь еще раз факты:
Я думаю, что проблема заключается в том, что, учитывая, что для каждого клиента существует одно условие (псевдокод: если (clientsqueue.isEmpty () -> wait) должно быть несколько условных переменных (я ошибаюсь?)
Дальнейшая информация:
ОС: Windows 7
Язык: C ++
Компилятор VS2010
Ваша настройка очень DBish. у вас есть таблица, представленная Clients
класс, и несколько экземпляров CLIENTOBJ
каждый действует как ряд таблицы, с id
действуя в качестве первичного ключа. Однако, насколько я понимаю, каждый клиент на самом деле является очередью данных.
Модель, используемую базами данных, может быть грубо описана как делегирование любого доступа к данным выделенному действию (потоку или процессу) в БД и отправка команд в него с использованием SQL. Проблемы синхронизации решаются с помощью транзакций и предложений SQL (обновление может совершенно не повлиять ни на одну строку, если id
не существует, но эта команда не потерпит неудачу, она просто вернет 0 обновленную строку). Возможно, в вашем случае будет интересна аналогичная модель: всего один глобальный мьютекс для представления транзакции, и каждый поток блокирует всю структуру данных, управляет ею и разблокирует. Однако это может быть не очень эффективно.
Асинхронный эквивалент состоит в том, чтобы каждая команда возвращала std::future
вместо реального результата. С этого момента поток должен ждать только на future
и действовать в соответствии с ним, когда он завершен (или сломан за исключением).
В пределах Clients
Например, любой вызов метода превращается в future
и promise
, Обещание помещается в очередь обещаний, и вызывающие потоки либо возвращают будущее из вызова метода, либо немедленно ожидают его.
С точки зрения процесса работы с БД это последовательная работа: у вас есть одна очередь обещаний, в которую все другие потоки отправляют данные, связанные с идентификатором клиента, к которому она должна перейти. Затем полученное обещание выполняется потоком БД в следующем порядке:
При использовании вышеуказанного решения все зависимости разделены, и задача оптимизирована.
Вы также можете посвятить одну тему CLIENTOBJ
, Затем поток БД становится потоком сортировки, который просто распределяет обещания каждому клиенту. Каждый клиент владеет ожидающей очередью чтения и данных данного id
Таким образом, при обработке обещаний нет блокировки.
Каждая очередь должна быть защищена мьютексом, что означает 1 мьютекс для основной очереди обещаний, 1 мьютекс для каждой очереди обещаний клиента и столько же условных переменных, сколько имеется потоков, использующих Clients
методы.
В моем ответе изначально предлагалось следующее:
Другими словами, вы могли бы заменить механизм будущее / обещание простой условной переменной, связанной с каждым потоком, не связанным с БД (будущее и обещание, вероятно, реализованы с использованием переменной cond., Но здесь вы бы сохранили накладные расходы на создание и уничтожение).
Но это делает некоторые неявные предположения о том, как CLIENTS
объект используется. Самая безопасная дорога действительно std::future
один.
Других решений пока нет …