#qt #pthreads #qthread
#qt #pthreads #qthread
Вопрос:
Может кто-нибудь сказать мне, почему этот qt-код не будет вызывать обратный вызов при определении ASYNC_TIMERS (т. Е. m_timer.start вызывается из pthread, но слот никогда не запускается). Очевидно, что это связано с вызовом из pthread, поскольку это работает, когда ASYNC_TIMERS не определен, но я хочу знать, как это исправить из pthread. Я перепробовал множество вещей, найденных в сети, включая moveToThread (), вызывающий запуск потоков, который вызывает exec (), но мне не повезло с этой проблемой?
Приветствия
multitimer.h:
#pragma once
#ifndef MULTI_TIMER_H
#define MULTI_TIMER_H
#include <QThread>
#include <QTimer>
#include <QMutex>
#include <QMap>
#include <QMetaType>
#include <cassert>
class MultiTimer : public QThread
{
Q_OBJECT
public:
typedef void (*multiTimerCallback)(quint32 p_id);
private:
QTimer m_timer;
QMutex m_mutex;
quint32 m_id;
multiTimerCallback m_callback;
void KillTimer(void);
public:
// only TimerFactory is allowed to instantiate MultiTimer
MultiTimer(quint32 p_id, multiTimerCallback p_callback);
~MultiTimer();
enum TTimerType
{
TT_SingleShot, ///< Timer fires only once
TT_Repetitive ///< Timer keeps firing repeatedly until stopped with KillTimer()
};
void SetTimer(quint32 p_delayInMilliseconds, MultiTimer::TTimerType timerType);
private slots:
void Update(void);
};
#endif
timer.cpp:
#include <QtCore/QCoreApplication>
#include "multitimer.h"
#include <stdio.h>
//--------------------------------------------------------------------------------------------------
void MultiTimer::SetTimer(quint32 p_delayInMilliseconds, MultiTimer::TTimerType timerType)
{
QMutexLocker locker(amp;m_mutex);
m_timer.setSingleShot(TT_SingleShot == timerType ? true : false);
m_timer.start(p_delayInMilliseconds);
//QTimer::singleShot(p_delayInMilliseconds, this,SLOT(Update()));
}
void MultiTimer::KillTimer(void)
{
QMutexLocker locker(amp;m_mutex);
m_timer.stop();
}
void MultiTimer::Update(void)
{
QMutexLocker locker(amp;m_mutex);
if (NULL != m_callback)
m_callback(m_id);
}
MultiTimer::MultiTimer(quint32 p_id, multiTimerCallback p_callback)
: m_id(p_id)
, m_callback(p_callback)
{
bool isConnected = true;
isConnected amp;= this->connect(amp;this->m_timer, SIGNAL(timeout()), this, SLOT(Update()), Qt::QueuedConnection);
assert(isConnected);
//this->start();
}
MultiTimer::~MultiTimer()
{
KillTimer();
wait();
}
//--------------------------------------------------------------------------------------------------
#define ASYNC_TIMERS
#define GLOBAL_TIMERS
void Callback(quint32 p_id)
{
printf("Got timered by timer %d.n", p_id);
}
MultiTimer *mt;
void StartTimers(void)
{
#ifndef GLOBAL_TIMERS
mt = new MultiTimer(1, Callback);
#endif
mt->SetTimer(1000, MultiTimer::TT_SingleShot);
}
#ifdef ASYNC_TIMERS
pthread_t AsyncTaskThread;
void *ProcessAsyncTasks(void */*ptr*/)
{
StartTimers();
return NULL;
}
#endif
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
#ifdef GLOBAL_TIMERS
mt = new MultiTimer(1, Callback);
#endif
#ifdef ASYNC_TIMERS
pthread_create(amp;AsyncTaskThread, NULL, amp;ProcessAsyncTasks, NULL);
#else
StartTimers();
#endif
return a.exec();
}
Комментарии:
1. Используете ли вы оба GLOBAL_TIMERS и ASYNC_TIMERS одновременно?
Ответ №1:
Я думаю, что у Threads и QObjects есть ответ: вы не можете использовать управляемые событиями объекты в другом потоке, отличном от того, где вы его создали.
В вашем коде, если GLOBAL_TIMERS
включена, вы будете создавать MultiTimer
в своем основном потоке, но вызывать m_timer.start()
в другом.
Процитируйте документы:
Объекты, управляемые событиями, могут использоваться только в одном потоке. В частности, это относится к механизму таймера и сетевому модулю. Например, вы не можете запустить таймер или подключить сокет в потоке, который не является потоком объекта.
Так что не делайте этого. (И пока вы этим занимаетесь, используйте QThread.)
Комментарии:
1. Я не вижу, чтобы он использовал события?
2.
m_timer.start(p_delayInMilliseconds);
будет генерировать события после некоторого ит (если это сработало в этом контексте, чего не происходит)3. Когда GLOBAL_TIMERS определен, это действительно работает. Это потому, что объект Multitimer был создан в потоке с циклом событий. Обратите внимание, что он спрашивает, почему это не работает, когда определен ASYNC_TIMERS. Эта версия не работает, потому что в потоке, в котором он создает объект MultiTimer, нет цикла событий.
4. @justanothercoder: Я не полностью согласен с этим. Ваш ответ правильный, но, судя по тому, как я прочитал документы, как с асинхронным, так и с глобальным таймерами (т. Е. с опубликованным кодом), он все равно запускал бы таймер из другого потока, который его создал.
5. Если я правильно прочитал его код с включенными GLOBAL_TIMERS, он не использует никаких потоков. И с помощью ASYNC_TIMERS он создает и использует объект MultiTimer из того же потока, который не является основным. Итак, из того, что я вижу, это не тот случай. РЕДАКТИРОВАТЬ — я прочитаю код еще раз 🙂
Ответ №2:
Вам нужен QEventLoop для обработки данных сигнала / слота в новом потоке.
Они нужны QTimer для работы.
void *ProcessAsyncTasks(void */*ptr*/)
{
QEventLoop loop;
StartTimers();
loop.exec();
return NULL;
}
Почему бы не использовать QThread?
Комментарии:
1. Я попробую это сделать, но это на самом деле неосуществимо для нашего окончательного проекта, поскольку мы получаем события из внешней библиотеки pthread. Я пытался использовать метод multitimers run (потому что это поток) для вызова exec, но это не сработало.
2. Я думаю, что вам следует еще раз взглянуть на то, что делают классы и что требуется для работы, и вы найдете лучший ответ. Мне трудно придумать что-то, что помогло бы, не зная, чего именно вам нужно достичь.
Ответ №3:
Хорошо, для тех, кто также пытается решить эту проблему, я думаю, что я понял это, если я перемещу объекты m_timer и multitimer обратно в основной поток qt и выдам сигнал запуска таймера «должным образом», кажется, что все работает (см. Ниже).
Возможно, я все еще мог бы выполнить вызов exec в qthread, чтобы заменить pthread и переместить в него multitimer и m_timer, но пока этот способ работает, и я получаю вывод «Установлено по таймеру % d. n» вовремя, даже после того, как pthread отключен, как и требовалось.:
Спасибо всем за информацию, если есть лучший способ сделать это или серьезная ошибка, которую я упустил из виду, было бы неплохо узнать? Приветствия
multitimer.h:
/**
@file multitimer.h
@brief Partial implementation of Windows-like SetTimer()/KillTimer() for Qt.
*/
#pragma once
#ifndef MULTI_TIMER_H
#define MULTI_TIMER_H
#include <QThread>
#include <QTimer>
#include <QMutex>
#include <QMap>
#include <QMetaType>
#include <cassert>
class MultiTimer : public QThread
{
Q_OBJECT
public:
typedef void (*multiTimerCallback)(quint32 p_id);
private:
QTimer m_timer;
QMutex m_mutex;
quint32 m_id;
multiTimerCallback m_callback;
void KillTimer(void);
public:
// only TimerFactory is allowed to instantiate MultiTimer
MultiTimer(quint32 p_id, multiTimerCallback p_callback);
~MultiTimer();
enum TTimerType
{
TT_SingleShot, ///< Timer fires only once
TT_Repetitive ///< Timer keeps firing repeatedly until stopped with KillTimer()
};
void SetTimer(quint32 p_delayInMilliseconds, MultiTimer::TTimerType timerType);
private slots:
void Update(void);
signals:
void start_sig(int);
};
#endif
timer.cpp:
#include <QtCore/QCoreApplication>
#include "multitimer.h"
#include <stdio.h>
//--------------------------------------------------------------------------------------------------
void MultiTimer::SetTimer(quint32 p_delayInMilliseconds, MultiTimer::TTimerType timerType)
{
QMutexLocker locker(amp;m_mutex);
m_timer.setSingleShot(TT_SingleShot == timerType ? true : false);
connect(this, SIGNAL(start_sig(int)), amp;m_timer, SLOT(start(int)), Qt::QueuedConnection);
//m_timer.start(p_delayInMilliseconds);
emit start_sig(p_delayInMilliseconds);
disconnect(this, SIGNAL(start_sig(int)), amp;m_timer, SLOT(start(int)));
}
void MultiTimer::KillTimer(void)
{
QMutexLocker locker(amp;m_mutex);
m_timer.stop();
}
void MultiTimer::Update(void)
{
QMutexLocker locker(amp;m_mutex);
if (NULL != m_callback)
m_callback(m_id);
}
MultiTimer::MultiTimer(quint32 p_id, multiTimerCallback p_callback)
: m_id(p_id)
, m_callback(p_callback)
{
bool isConnected = true;
isConnected amp;= this->connect(amp;this->m_timer, SIGNAL(timeout()), this, SLOT(Update()), Qt::QueuedConnection);
assert(isConnected);
//this->start();
moveToThread(qApp->thread());
m_timer.moveToThread(qApp->thread());
}
MultiTimer::~MultiTimer()
{
KillTimer();
wait();
}
//--------------------------------------------------------------------------------------------------
#define ASYNC_TIMERS
#define xGLOBAL_TIMERS
void Callback(quint32 p_id)
{
printf("Got timered by timer %d.n", p_id);
}
MultiTimer *mt;
void StartTimers(void)
{
#ifndef GLOBAL_TIMERS
mt = new MultiTimer(1, Callback);
#endif
mt->SetTimer(2000, MultiTimer::TT_SingleShot);
}
#ifdef ASYNC_TIMERS
pthread_t AsyncTaskThread;
void *ProcessAsyncTasks(void */*ptr*/)
{
StartTimers();
return NULL;
}
#endif
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
#ifdef GLOBAL_TIMERS
mt = new MultiTimer(1, Callback);
#endif
#ifdef ASYNC_TIMERS
pthread_create(amp;AsyncTaskThread, NULL, amp;ProcessAsyncTasks, NULL);
#else
StartTimers();
#endif
return a.exec();
}
Комментарии:
1. Чего вы пытаетесь достичь? Я не могу вам помочь, пока не пойму, чего вы хотите. Если все, что вам нужно, это QTimer, который вызывает некоторую функцию с некоторым интервалом, вам вообще не нужно создавать потоки для этого. Если вы хотите, чтобы QTimers запускались из разных потоков, вам нужно, чтобы QEventLoop запускался в этих потоках. Это то, что у вас есть в вашем основном потоке, поскольку QCoreApplication предоставляет это.