2014-05-28 4 views
1

Есть ли способ выполнить секцию кода в ожидании блокировки мьютекса?Процесс во время ожидания блокировки мьютекса

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

Например, я хотел бы код, чтобы выглядеть примерно так, в псевдо:

boost::unique_lock<boost::mutex> process_lock(process_mutex, work_while_contending); 
//code to execute while lock contending 
process_lock.proceed_after_lock(); 

Я рассмотрел подталкивание-х synchronization section, и в то время как futures и recursive звук, как мое намерение может быть достигнуто, но я не может понять, как реализовать свои намерения.

Как можно реализовать мои намерения?

ответ

3

Вы не хотите этого делать в большинстве случаев. Если вы хотите сделать это, вы, вероятно, хотите, чтобы использовать фьючерсы, как это:

auto f = std::async(std::launch::async, [&]() { 
    // do work in another thread here. 
}); 

boost::unique_lock<boost::mutex> process_lock(process_mutex); 
auto result = f.get(); 

// proceed here the work is done and you have the lock 

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

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

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

+0

Async заставляет работу выполнять в другом потоке. При вызове get() этот поток блокируется до тех пор, пока не закончится асинхронный поток. Итак, это еще один поток, который выполняет эту работу. Исходный поток просто «запустил async, дождитесь mutex, дождитесь async». – SoapBox

+0

Очень хороший момент о голоде с подходом 'try_lock'. В то время как надменные муфтеки не гарантируют, что голодание ничьей не будет происходить при нормальном использовании, вероятность этого должна быть увеличена в этом случае. Тем не менее, если основной целью здесь является повышение производительности, накладные расходы на разворачивание отдельного рабочего потока для каждого вызова могут устранить любые реальные выигрыши в этом случае. Это действительно зависит от того, насколько тяжелым является соперничество, сколько времени удерживаются блокировки и как долго выполняется эта задача ожидания, какой подход в этом случае более уместен. –

2

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

boost::unique_lock<boost::mutex> process_lock(process_mutex, boost::defer_lock_t); 
while(!process_lock.try_lock()) 
{ 
    ...do some work 
} 

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

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

boost::unique_lock<boost::mutex> process_lock(process_mutex, boost::try_to_lock_t); 
if(!process_lock.owns_lock()) 
{ 
    ...do some work 
    process_lock.lock(); 
} 
Смежные вопросы