Я использую библиотеку C ++ boost :: thread, что в моем случае означает, что я использую pthreads. Официально мьютекс должен быть разблокирован из того же потока, который его блокирует, и я хочу получить эффект блокировки одного потока, а затем разблокировки другого. Есть много способов сделать это. Одна возможность - написать новый класс мьютекса, который допускает такое поведение.
Например:
class inter_thread_mutex{
bool locked;
boost::mutex mx;
boost::condition_variable cv;
public:
void lock(){
boost::unique_lock<boost::mutex> lck(mx);
while(locked) cv.wait(lck);
locked=true;
}
void unlock(){
{
boost::lock_guard<boost::mutex> lck(mx);
if(!locked) error();
locked=false;
}
cv.notify_one();
}
// bool try_lock(); void error(); etc.
}
Следует отметить, что приведенный выше код не гарантирует доступ к FIFO, поскольку, если один поток вызывает lock (), а другой вызывает unlock (), этот первый поток может получить блокировку раньше других ожидающих потоков. (Если подумать, документация boost :: thread не дает никаких явных гарантий планирования для мьютексов или условных переменных). Но давайте просто пока проигнорируем это (и любые другие ошибки).
У меня вопрос: если я решу пойти по этому пути, смогу ли я использовать такой мьютекс в качестве модели для концепции Lockable Boost. Например, что-нибудь пойдет не так, если я использую boost::unique_lock< inter_thread_mutex >
для доступа в стиле RAII, а затем передам эту блокировку boost::condition_variable_any.wait()
и т. Д.
С одной стороны, я не понимаю, почему нет. С другой стороны, «я не понимаю, почему нет», как правило, очень плохой способ определить, будет ли что-то работать.
Причина, по которой я спрашиваю, состоит в том, что если выяснится, что мне нужно написать классы-обертки для блокировок RAII и условных переменных и всего остального, то я бы предпочел просто найти другой способ достижения того же эффекта.
EDIT:
Тип поведения, который я хочу, в основном следующий. У меня есть объект, и он должен быть заблокирован всякий раз, когда он изменяется. Я хочу заблокировать объект из одного потока и поработать над ним. Затем я хочу сохранить объект заблокированным, пока говорю другому рабочему потоку завершить работу. Таким образом, первый поток может продолжаться и делать что-то еще, пока рабочий поток завершает работу. Когда рабочий поток завершается, он разблокирует мьютекс.
И я хочу, чтобы переход был безразличным, чтобы никто другой не смог установить блокировку мьютекса между тем, когда поток 1 начинает работу, а поток 2 завершает ее.
Кажется, что-то вроде inter_thread_mutex будет работать, и это также позволит программе взаимодействовать с ним, как если бы это был обычный мьютекс. Так что похоже на чистое решение. Если есть лучшее решение, я буду рад это услышать.
ИЗМЕНИТЬ СНОВА:
Причина, по которой мне нужны блокировки, для начала состоит в том, что существует несколько главных потоков, и блокировки существуют для предотвращения одновременного доступа к общим объектам недопустимыми способами.
Таким образом, код уже использует последовательность операций без блокировки на уровне цикла на уровне главного потока. Кроме того, в исходной реализации не было рабочих потоков, а мьютексы были обычными кошерными мьютексами.
inter_thread_thingy был задуман как оптимизация, в первую очередь для улучшения времени ответа. Во многих случаях было достаточно гарантировать, что «первая часть» операции A будет происходить до «первой части» операции B. В качестве глупого примера, скажем, я ударил объект 1 и дал ему черный глаз. Затем я говорю объекту 1 изменить его внутреннюю структуру, чтобы отразить все повреждения ткани. Я не хочу ждать повреждения ткани, прежде чем перейти к удару объекта 2. Однако я хочу, чтобы повреждение ткани происходило как часть одной и той же операции; например, тем временем я не хочу, чтобы какой-либо другой поток перенастраивал объект таким образом, чтобы повреждение ткани было недопустимой операцией. (да, этот пример во многом несовершенен, и нет, я не работаю над игрой)
Итак, мы внесли изменения в модель, в которой владение объектом может быть передано рабочему потоку для завершения операции, и на самом деле это работает довольно хорошо; каждый главный поток может выполнить намного больше операций, потому что ему не нужно ждать завершения их всех. И, поскольку последовательность событий на уровне главного потока все еще основана на цикле, легко написать высокоуровневые операции главного потока, поскольку они могут основываться на предположении о завершении операции (точнее, критической) первая часть ", от которой зависит логика последовательности, завершается), когда соответствующий вызов функции возвращается.
Наконец, я подумал, что было бы неплохо использовать inter_thread mutex / семафорные штуковины с использованием RAII с буст-блокировками для инкапсуляции необходимой синхронизации, которая необходима для того, чтобы все это работало.