LINUX.ORG.RU

Как найти зависший QMutex.

 , , ,


0

2

Есть класс для работы с девайсом, который помещается в 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 тут начать работать параллельно и из-за локов мьютексами друг друга зависать ?



Последнее исправление: user2132 (всего исправлений: 1)
Ответ на: комментарий от user2132

А как сделать защиту мьютексами

Код, который работает с листом должен быть прикрыт мьютексом. Мы это обсудили в твоей предыдущей теме. Ничего нового.

Также есть fun_basic, которая тоже с этим QList работает и может вызывать fun_i разные.

Кажется, я догадываюсь почему у тебя потоки зависают. Ты делаешь так?

void fun_basic()
{
    QMutexLocker lock(mutex);
    // Здесь работаешь с листом
    
    // Вызов другого метода, который работает тоже с листом
    fun_i(); 
}

void fun_i()
{
    QMutexLocker lock(mutex); // <--- Опачки повторное взятие мьютекса
    // Здесь работаешь с листом
}

Если в подобном коде вызвать fun_basic, то будет вечная блокировка внутри fun_i.

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

Нет, именно так не делаю, но все равно где-то ловлю блокировку.

user2132
() автор топика
Ответ на: комментарий от ox55ff

Единственное, что заметил, это fun_basic вызывается по таймеру, у которого стояло start(0), могло это вызывать fun_basic следующую, пока там идет предыдущая еще ?

user2132
() автор топика
Ответ на: комментарий от ox55ff

А как защиту поставить на fun_i и на fun_basic, у меня могут быть вызовы из других классов типа

worker->fun_i();
а может быть вызов fun_i внутри своего класса из fun_basic. То есть мне нужно определять как-то откуда был вызов, чтобы принимать решение блочить или нет мьютексом. То есть если из fun_basic был вызов в fun_i, то я буду пропускать мьютекс. На fun_basic будет всегда мьютекс
QMutexLocker lock(mutex);
.

user2132
() автор топика
Ответ на: комментарий от user2132

Можно рекурсивный мьютекс вонзить, но это архитектурно криво выглядит.

Можно передавать в 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. А то так путаница возникает. Самому же сложно за всем следить.

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

Нашел багу тут:

void Work2::setSensorValue()
{
    qDebug() << "wait setSensorValue";
    QMutexLocker locker(&mutex_1);
    qDebug() << "after mutex setSensorValue";

    for(int i=0;i<10;i++)
    {
        qDebug() << "try emit sig_TemperatureChanged" << i;
        emit sig_TemperatureChanged();
        qDebug() << "after emit sig_TemperatureChanged" << i;


    }
}

void Work2::clearAdcSpreading()
{
        qDebug() << "wait clearAdcSpreading";
        QMutexLocker locker(&mutex_1);
        qDebug() << "after mutex clearAdcSpreading";


}

У меня setSensorValue вызывалась. Сигнал sig_TemperatureChanged связан со слотом clearAdcSpreading. Висло на clearAdcSpreading, но я полагал, что сигналы просто встанут в очередь и после выхода из цикла в setSensorValue мьютех освободится, и все эти сигналы исполнятся. Но виснет сразу после первого. Можете объяснить почему именно так работает ? Сигнал надо было как метод рассматривать ?

user2132
() автор топика
Ответ на: комментарий от user2132

Сигналы встанут в очередь только при Qt::queuedConnection. Ты когда делаешь connect явно пропиши, что хочешь: Qt::queuedConnection или Qt::directConnection. В последнем случае это всё равно что прямой вызов метода.

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff
void MyWidget::setTemp(double temp)
{
	QMutexLocker lock(&mutex);
    Temperature = temp;
}

void MyWidget::setState(const QString &state)
{
	QMutexLocker lock(&mutex);
    Status = state;
}
void MyWidget::setList(QList<Myclass *> *list)
{
	QMutexLocker lock(&mutex);
    _list = list;
}

void MyWidget::paintEvent(QPaintEvent *)  //метод отрисовки
{

	QMutexLocker lock(&mutex);
	
	//чтение только _list, Status, Temperature
}

А нужно ли тут все эти методы защищать мьютексом от крэша или на setTemp упасть не сможет из-за double типа ? Методы setTemp, setState, setList вызываются другим тредом через сигналы

user2132
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.