Есть класс для работы с девайсом, который помещается в QThread, в конструкторе класса объявлен таймер так:
timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(sl_timer()));
timer->start(0); //для наиболее частого опроса девайса 0 стоит.
sl_timer - это метод этого же класса, в котором идет опрос девайса. Этот метод и другие методы, которые вызываются из sl_timer и работают с глобальным QList защищены QMutex так:
QLockerMutex lock(&mutex);
В одном из 5-10 случаев происходят какие-то зависания внутри sl_timer, полагаю что мьютекс где-то зависает. Как бы найти в какой конкретно функции это происходит ?
Правильно ли я понимаю, что sl_timer из-за start(0) сможет вызваться второй раз еще когда первый вызов не отработал. То есть могут ли sl_timer тут начать работать параллельно и из-за локов мьютексами друг друга зависать ?
Вопрос зачем вы все смешали? Если используете потоки - то вам не нужен таймер, просто заставляйте поток заснуть на нужное время. Дальше таймер в ноль - замечательно, это аналогично while(true) - он тарабанет постоянно держа ресурс и не давая другим потокам захватить его.
Мне нужно результаты работы этого класса кидать периодически в другой класс, для этого там таймеры заведены. Про таймер с 0 да, думаю это плохая идея, хочу поставить что-то типа 100 мс.
для этого есть сигналы и слоты ручной вызов сигнала emit. Если вам надо опрашивать датчик и время выполнения этого опроса может занять очень много времени отправьте в новый поток что то вроде такой функции
while(true){
опрашиваем датчик
emit sendData(Данные); это сигнал (ваш сигнал объявлен в секции signals:), в основном классе свяжите его со слотом приемником.
QThread::usleep(микросекунды);
}
А можешь объяснить как это сделать ? Я запускаю из qt под отладкой, как по потокам ее посмотреть ? Имелось ввиду ставить бряки и ловить где потоки в данный момент выполняются ? Или можно как-то получить из qt эту инфу по всем потокам сразу.
Если используешь QtCreator, то запусти приложение под отладкой, дождись пока оно зависнит и через меню Debug -> Interrupt останови выполнение. У тебя будет стек вызовов для всех потоков.
А как с таймерами работать внутри класса, наследующегося от QObject и помещенного MoveToThread.
Если втыкаю в слоте этого класса while(1); то таймер, который перед этим запускаю не срабатывает пока слот не отвиснет.
Тимеры объявляю внутри этого класса так:
timer= new QTimer(this); и делаю connect к слоту в этом классе.
А лучше, сходи на лекториум и посмотри лекции по многопоточному программированию. Перед этим - можешь положить в голову мысль, что таймер это либо событие в очереди, либо условная переменная со сном по таймауту, либо их комбинация. Остальные случаи тебе пока рассматривать не нужно.
Да в конструкторе повисает в нем и далее ничего не выполняется там из-за этого. Но если в слот пихаю, где запускаю таймеры, то также не работает.
Вот такой вариант:
Так while(1) для симуляции ошибки зависания того слота.
То есть ставить там _loop.processEvents(); это ведь испортит симуляцию с реальным случаем (да с ней работает, но идея была просто просимулировать зависший слот, чтобы по таймеру все грохнуть там). В реальной проге там виснет в каком-то методе на строке
Окей, с dealock я понял. Я теперь про общий случай, вот слот повис в каком-нибудь цикле из-за ошибки, пусть там не мьютекс, а просто из цикла не вышел. Как мне сделать на этот случай, чтобы таймеры отработали. Так как если они сработают, то главный класс получит инфу о проблеме и еще раз пошлет запрос так:
Никак. Если поток повис, то тебе нужен другой. Но QObject не может быть в двух потоках одновременно. Разбирайся с мьютексами.
Если у тебя потоки блокируются на мьютексе, то QEventLoop тоже никак не поможет.
Вечная блокировка может быть по причине, что поток взял мьютекс и завис, а другие потоки сидят в ожидании. Другой вариант, что у тебя один и тот же поток из-за рекурсии пытается дважды взять мьютекс. Тут может помочь QRecursiveMutex. Но лучше разобраться почему происходит рекурсия, если она явно тобой не предусмотрена.
А как сделать защиту мьютексами если есть функции fun_1 ... fun_N, которые могут вызываться из других классов, они все с QList работают. Также есть fun_basic, которая тоже с этим QList работает и может вызывать fun_i разные. Как в этом случае их защищать, fun_i не вызывает fun_j.
Код, который работает с листом должен быть прикрыт мьютексом. Мы это обсудили в твоей предыдущей теме. Ничего нового.
Также есть fun_basic, которая тоже с этим QList работает и может вызывать fun_i разные.
Кажется, я догадываюсь почему у тебя потоки зависают. Ты делаешь так?
void fun_basic()
{
QMutexLocker lock(mutex);
// Здесь работаешь с листом
// Вызов другого метода, который работает тоже с листом
fun_i();
}
void fun_i()
{
QMutexLocker lock(mutex); // <--- Опачки повторное взятие мьютекса
// Здесь работаешь с листом
}
Если в подобном коде вызвать fun_basic, то будет вечная блокировка внутри fun_i.
Единственное, что заметил, это fun_basic вызывается по таймеру, у которого стояло start(0), могло это вызывать fun_basic следующую, пока там идет предыдущая еще ?
А как защиту поставить на fun_i и на fun_basic, у меня могут быть вызовы из других классов типа
worker->fun_i();
а может быть вызов fun_i внутри своего класса из fun_basic. То есть мне нужно определять как-то откуда был вызов, чтобы принимать решение блочить или нет мьютексом.
То есть если из fun_basic был вызов в fun_i, то я буду пропускать мьютекс. На fun_basic будет всегда мьютекс
Можно рекурсивный мьютекс вонзить, но это архитектурно криво выглядит.
Можно передавать в fun_i булевый параметр, который будет говорить нужно ли брать блокировку. Только тут нужно быть аккуратней с QMutexLocker. Если он будет внутри блока if, то после выхода из этого блока блокировка снимется. Ещё стоит отметить, что если этот метод публичный, то это тоже архитектурное говно. Не надо рассчитывать, что внешний код знает особенности внутреннего устройства класса.
Можно разделить метод fun_i на два: fun_i и fun_i_private. fun_i будет брать блокировку и вызывать fun_i_private, в котором находится вся логика, но уже без блокировки. Соответственно, если fun_basic уже взял блокировку, то он может напрямую вызвать fun_i_private, а остальные будут работать через fun_i.
Короче раздели интерфейс своего класса на thread-safe и не thread-safe. А то так путаница возникает. Самому же сложно за всем следить.
QEventLoop::exec это то на чём блокируется QApplication::exec например, дальше учи матчасть. Например - возьми и напиши цикл обработки сообщений руками разок хотя бы.
Не занимайся херней, потрать время на нормальное вникание в тему, а не абы натыкать…
Вот этого товарища пару лекций послушай и упражнения выполни. Таненбаума про операционные системы хотя бы первые три главы осиль. Руками примитивы попиши, глянь сорцы QEventLoop и QApplication.
По usb подключен. Опрос идет максимально часто, так как нужно за определенное время собрать нужное число данных. Поэтому start(0) был таймер. Также требуется полученные данные выводить в gui раз в секунду. Для переброса в gui класс данные идут по сигналам.
Вот как лучше организовать, чтобы максимально часто опрашивать и обновлять gui.
Есть ли смысл в таких задачах для опроса делать отдельный консольный процесс, с которым обмен данными будет между основной программой для отображения данных в gui.
У меня setSensorValue вызывалась. Сигнал sig_TemperatureChanged связан со слотом clearAdcSpreading. Висло на clearAdcSpreading, но я полагал, что сигналы просто встанут в очередь и после выхода из цикла в setSensorValue мьютех освободится, и все эти сигналы исполнятся.
Но виснет сразу после первого.
Можете объяснить почему именно так работает ? Сигнал надо было как метод рассматривать ?
Сигналы встанут в очередь только при Qt::queuedConnection. Ты когда делаешь connect явно пропиши, что хочешь: Qt::queuedConnection или Qt::directConnection. В последнем случае это всё равно что прямой вызов метода.
А нужно ли тут все эти методы защищать мьютексом от крэша или на setTemp упасть не сможет из-за double типа ?
Методы setTemp, setState, setList вызываются другим тредом через сигналы
если нет:
как ты работаешь с девайсом: по serial порту или по tcp/ip? (да, не читал весь тред)
если да: на винде или на лине?
если на винде, если гора костылей, которые помогут.
всё это чисто касательно qt и qiodevice
напрямую связано с тем, как ты их юзаешь с сигналами/слотами/таймерами. особенно под виндой. там c Qt::CoarseTimer происходит полный ад и израиль
event loop не трогай ни в коем случае. тем более не дёргай qApp->processEvents() (никогда)
посмотри лучше на семафоры. имхо для твоей задачи они более актуальны. если лень, замени все QMutexLocker(…) на tryLock() { qt_noop(); } и лови свои говноподелки. но это не путь ниндзя.