Отказ от ответственности: я задал этот вопрос несколько дней назад на codereview, но не получил ответа. Здесь я меняю формат вопроса на запрос проверки на конкретный проблемы.
Я разрабатываю видеоплеер со следующим дизайном:
Основной поток - это поток GUI (Qt SDK).
Второй поток - нить игрока, который принимает команды из потока GUI для воспроизведения, перемотки вперед, назад, остановки и т.д. Теперь этот поток работает в постоянном цикле и использует мьютексы и условия ожидания для синхронизации в реальном времени с командами основного потока.
У меня есть 2 проблемы с этим кодом:
Я не чувствую, что мой дизайн абсолютно прав: я использую как блокировки мьютекса, так и атомные переменные. Интересно, могу ли я оставаться только с атомами и использовать блокировки только для установки условий ожидания.
Я испытываю непоследовательные ошибки (вероятно, из-за гонки состояния, когда команда воспроизведения пытается заблокировать мьютекс, который уже заблокирован потоком во время работы цикла воспроизведения), когда я запускаю команды "play", которые активируют цикл внутри петля потока. Поэтому я полагаю, что он блокирует доступ к общим переменным в основной поток.
Я удалил код из ненужного материала, и он обычно выглядит следующим образом:
void PlayerThread::drawThread()//thread method passed into new boost::thread
{
//some init goes here....
while(true)
{
boost::unique_lock<boost::mutex> lock(m_mutex);
m_event.wait(lock); //wait for event
if(!m_threadRun){
break; //exit the tread
}
///if we are in playback mode,play in a loop till interrupted:
if(m_isPlayMode == true){
while(m_frameIndex < m_totalFrames && m_isPlayMode){
//play
m_frameIndex ++;
}
m_isPlayMode = false;
}else{//we are in a single frame play mode:
if(m_cleanMode){ ///just clear the screen with a color
//clear the screen from the last frame
//wait for the new movie to get loaded:
m_event.wait(lock);
//load new movie......
}else{ //render a single frame:
//play single frame....
}
}
}
}
Вот функции-члены вышеуказанного класса, которые отправляют команды в цикл потока:
void PlayerThread::PlayForwardSlot(){
// boost::unique_lock<boost::mutex> lock(m_mutex);
if(m_cleanMode)return;
m_isPlayMode = false;
m_frameIndex++;
m_event.notify_one();
}
void PlayerThread::PlayBackwardSlot(){
// boost::unique_lock<boost::mutex> lock(m_mutex);
if(m_cleanMode)return;
m_isPlayMode = false;
m_frameIndex-- ;
if(m_frameIndex < 0){
m_frameIndex = 0;
}
m_event.notify_one();
}
void PlayerThread::PlaySlot(){
// boost::unique_lock<boost::mutex> lock(m_mutex);
if(m_cleanMode)return;
m_isPlayMode = true;
m_event.notify_one(); //tell thread to start playing.
}
Все члены флага, такие как m_cleanMode, m_isPlayMode и m_frameIndex, являются атомами:
std::atomic<int32_t> m_frameIndex;
std::atomic<bool> m_isPlayMode;
std::atomic<bool> m_cleanMode;
Сводка вопросов:
-
Нужно ли мне блокировать мьютексы при использовании атомистики?
-
Я устанавливаю ожидание в правильном месте внутри цикла while нить?
-
Любое предложение лучшего дизайна?
UPDATE:
Хотя я получил ответ, который, кажется, находится в правильном направлении, я действительно не понимаю его. Особенно часть псевдокода, которая говорит об обслуживании. Мне совершенно непонятно, как это будет работать. Мне хотелось бы чтобы получить более подробный ответ. Также странно, что я получил только один конструктивный ответ на такую общую проблему. Поэтому я возвращаю награду.