踏青山丶C++浑浑噩噩的一天
Author:AoyamaRyo
EMail:wind.river.sun@gmail.com
以前,一个类实例总在单个线程中使用,从没考虑过类与多线程的关系。Author:AoyamaRyo
EMail:wind.river.sun@gmail.com
现在想写一个Station类,其包含数据采集成员函数(单独一个线程),数据上传成员函数(单独一个线程),该类实例本身存在于主线程(GUI)中。糊里糊涂尝试了一通,最后还是乖乖地用Qt封装的类,其中原理也未深究,为赶时间也颇为无奈。
可以通过下面这种方式将一个对象的两个成员函数置于两个线程中执行,但该不该这样用也不太清楚。
点击(此处)折叠或打开
-
pid_t gettid(void)
-
{
-
return syscall(__NR_gettid);
-
}
-
-
class Test
-
{
-
public:
-
explicit Test(void){
-
}
-
void f1(){
-
printf("In f1():%x\n",gettid());
-
while(1){}
-
return;
-
}
-
void f2(){
-
printf("In f2():%x\n",gettid());
-
while(1){}
-
return;
-
}
-
};
-
-
class Test *a;
-
- void *F3(void *p){
-
a = new Test;
-
}
-
- void *F1(void *p){
-
(static_cast(p))->f1();
-
}
- void *F2(void *p){
-
(static_cast(p))->f2();
-
}
-
-
int main()
-
{
-
pthread_t t1,t2,t3;
-
printf("Main thread ID: %x\n",gettid());
-
pthread_create(&t3,NULL,F3,NULL);
-
sleep(1);
-
pthread_create(&t1,NULL,F1,a);
-
pthread_create(&t2,NULL,F2,a);
-
while(1){}
-
return 0;
- }
结果如下:
Main thread ID: 1440
In f1():1442
In f2():1443
(一)封装QThread
定义两个类Uploader、Capturer皆继承QThread,分别重载其run()虚函数完成各自业务逻辑,在Station类中将这两个类的实例定义作成员。而后,对共享的内存区域进行保护。
读书不多,不知道这个方法有没有问题。
读书不多,不知道这个方法有没有问题。
(二)Station的成员函数置于单独线程
将完成数据采集以及上传功能的Station成员函数capture()、upload()置于单独线程中(使用pthread_create()创建线程)。遇到的问题见示例代码中的注释。
///MainWindow.h
#include
#define gettid() syscall(__NR_gettid)
classMainWindow : public QMainWindow{
Q_OBJECT
public:explicit MainWindow(QWidget *parent = 0);
~MainWindow();
//void*hdlThread(void *args); //只有类的静态函数能作为pthread_create()创建线程的回调函数
//静态函数没有this指针。瞎胡闹,有就出乱子了。
//只好将Station实例指针(this)保存在文件变量(struct ThreadArgs thrArg)中。
///MainWindow.h
#include
#define gettid() syscall(__NR_gettid)
classMainWindow : public QMainWindow{
Q_OBJECT
public:explicit MainWindow(QWidget *parent = 0);
~MainWindow();
//void*hdlThread(void *args); //只有类的静态函数能作为pthread_create()创建线程的回调函数
//静态函数没有this指针。瞎胡闹,有就出乱子了。
//只好将Station实例指针(this)保存在文件变量(struct ThreadArgs thrArg)中。
public slots: void sltTestTimer();
//signals:
// voidsigTimeOut(); //signals限定的成员函数是“protect”属性,非MainWindow成员函数无法通过调用
//emit sigTimeOut() ,企图调用其sltTestTimer()槽函数。
private:Ui::MainWindow *ui;
pthread_tmChildTid;
};
///MainWindow.c
QTimer *tTimer;
struct ThreadArgs{
MainWindow*__this;
int interval;
};
struct ThreadArgs thrArg;
#ifdef TEST
void *hdlThread(void *arg){ //线程回调函数,但该函数中tTimer->start();是无法执行的,它需要事件 //循环,需要在QThread创建的线程中执行。
//qDebug()<< (unsigned int)(thrArg.__this) <<"\n";
tTimer = new QTimer;tTimer->setInterval(thrArg.interval);
QObject::connect(tTimer,SIGNAL(timeout()),
thrArg.__this,SLOT(sltTestTimer()));
tTimer->start();
while(1){}
}
#else
voidsigroutine(int signo){ //所以使用Linux系统提供的定时器,实现周期性作业
switch(signo){
caseSIGALRM:
thrArg.__this->sltTestTimer(); ///通过打印线程tid发现,这个槽函数总是运行在主线程中
///这不是乐见的,但却是理所当然的,signal()、sigaction()函数设定的是
///整个进程的信号处理,可以通过phread_sigmask()在其他线程中屏蔽该
///SIGALARM信号。但这样做真的好吗?感觉有些跛脚。
signal(SIGALRM,sigroutine);
break;
}
return;
}
void *hdlThread(void *arg){
struct itimerval value, ovalue;
signal(SIGALRM, sigroutine);
value.it_value.tv_sec = thrArg.interval;
value.it_value.tv_usec = 0;
value.it_interval.tv_sec = thrArg.interval;
value.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &value, &ovalue);
for(;;);
}
#endif
int threadId;
QReadWriteLockstrLock;
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(newUi::MainWindow){
ui->setupUi(this);
thrArg.__this = this;
thrArg.interval = 4;
int ret =pthread_create(&mChildTid,NULL,hdlThread,NULL);
}
MainWindow::~MainWindow(){ delete ui;}
void MainWindow::sltTestTimer(){
QWriteLockertmpLock(&strLock); //利用C++类构造、析构函数特性自动解锁临界资源
threadId = gettid();
qDebug()<< "Timer is working in thread: "<< threadId <<"...\n";
}
/*-------------------------------不用信号,实现定时器,只要稍加修改传入回调函数即可-------------------------*/
头文件:
#ifndefCTIMER_H_
#define CTIMER_H_
#include
#include
classCTimer
{
private:pthread_t thread_timer;
long m_second, m_microsecond;
static void *OnTimer_stub(void *p)
{
(static_cast<CTimer*>(p))->thread_proc(); ///去掉this指针,否则pthread_create()不认该函数。
}
void thread_proc();
virtual void OnTimer();
public:CTimer();
CTimer(long second, long microsecond);
virtual~CTimer();
voidSetTimer(longsecond,long microsecond);
void StartTimer();
voidStopTimer();
};
#endif /* CTIMER_H_ */
源文件:
#include"CTimer.h"
#include
#include
#include
#include
usingnamespace std;
//////////////////////////publicmethods//////////////////////////
CTimer::CTimer(): m_second(0), m_microsecond(0){}
CTimer::CTimer(long second, long microsecond) : m_second(second), m_microsecond(microsecond){}
CTimer::~CTimer(){}
void CTimer::SetTimer(long second, long microsecond){
m_second = second;
m_microsecond =microsecond;
}
void CTimer::StartTimer(){
pthread_create(&thread_timer, NULL, OnTimer_stub, this);
}
void CTimer::StopTimer(){
pthread_cancel(thread_timer);
pthread_join(thread_timer, NULL); //wait the thread stopped
}
//////////////////////////privatemethods//////////////////////////
voidCTimer::thread_proc(){
while (true) {
OnTimer(); ///问题变成,OnTimer()-last-end至OnTimer-next-start的时间间隔
///为tempval。期望的是OnTimer()-last-start至OnTimer-next-start
///的时间间隔为tempval,如此必然要使用线程间通信。
/* 加锁、解锁的时间消耗对该系统而言不足挂齿。可以用锁实现:
添加成员:QMutex mTimerLock;
void waitToStart(){
mTimerLock.lock();
}
bool isReadyToStart(){
return mTimerLock.trylock();
}
修改thread_proc()函数,睡眠结束后只解锁mTimerLock,继续休眠。
只是再注意一下回调函数处理时间比定时时间长的情况。
*/
//signals:
// voidsigTimeOut(); //signals限定的成员函数是“protect”属性,非MainWindow成员函数无法通过调用
//emit sigTimeOut() ,企图调用其sltTestTimer()槽函数。
private:Ui::MainWindow *ui;
pthread_tmChildTid;
};
///MainWindow.c
QTimer *tTimer;
struct ThreadArgs{
MainWindow*__this;
int interval;
};
struct ThreadArgs thrArg;
#ifdef TEST
void *hdlThread(void *arg){ //线程回调函数,但该函数中tTimer->start();是无法执行的,它需要事件 //循环,需要在QThread创建的线程中执行。
//qDebug()<< (unsigned int)(thrArg.__this) <<"\n";
tTimer = new QTimer;tTimer->setInterval(thrArg.interval);
QObject::connect(tTimer,SIGNAL(timeout()),
thrArg.__this,SLOT(sltTestTimer()));
tTimer->start();
while(1){}
}
#else
voidsigroutine(int signo){ //所以使用Linux系统提供的定时器,实现周期性作业
switch(signo){
caseSIGALRM:
thrArg.__this->sltTestTimer(); ///通过打印线程tid发现,这个槽函数总是运行在主线程中
///这不是乐见的,但却是理所当然的,signal()、sigaction()函数设定的是
///整个进程的信号处理,可以通过phread_sigmask()在其他线程中屏蔽该
///SIGALARM信号。但这样做真的好吗?感觉有些跛脚。
signal(SIGALRM,sigroutine);
break;
}
return;
}
void *hdlThread(void *arg){
struct itimerval value, ovalue;
signal(SIGALRM, sigroutine);
value.it_value.tv_sec = thrArg.interval;
value.it_value.tv_usec = 0;
value.it_interval.tv_sec = thrArg.interval;
value.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &value, &ovalue);
for(;;);
}
#endif
int threadId;
QReadWriteLockstrLock;
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(newUi::MainWindow){
ui->setupUi(this);
thrArg.__this = this;
thrArg.interval = 4;
int ret =pthread_create(&mChildTid,NULL,hdlThread,NULL);
}
MainWindow::~MainWindow(){ delete ui;}
void MainWindow::sltTestTimer(){
QWriteLockertmpLock(&strLock); //利用C++类构造、析构函数特性自动解锁临界资源
threadId = gettid();
qDebug()<< "Timer is working in thread: "<< threadId <<"...\n";
}
/*-------------------------------不用信号,实现定时器,只要稍加修改传入回调函数即可-------------------------*/
头文件:
#ifndefCTIMER_H_
#define CTIMER_H_
#include
#include
classCTimer
{
private:pthread_t thread_timer;
long m_second, m_microsecond;
static void *OnTimer_stub(void *p)
{
(static_cast<CTimer*>(p))->thread_proc(); ///去掉this指针,否则pthread_create()不认该函数。
}
void thread_proc();
virtual void OnTimer();
public:CTimer();
CTimer(long second, long microsecond);
virtual~CTimer();
voidSetTimer(longsecond,long microsecond);
void StartTimer();
voidStopTimer();
};
#endif /* CTIMER_H_ */
源文件:
#include"CTimer.h"
#include
#include
#include
#include
usingnamespace std;
//////////////////////////publicmethods//////////////////////////
CTimer::CTimer(): m_second(0), m_microsecond(0){}
CTimer::CTimer(long second, long microsecond) : m_second(second), m_microsecond(microsecond){}
CTimer::~CTimer(){}
void CTimer::SetTimer(long second, long microsecond){
m_second = second;
m_microsecond =microsecond;
}
void CTimer::StartTimer(){
pthread_create(&thread_timer, NULL, OnTimer_stub, this);
}
void CTimer::StopTimer(){
pthread_cancel(thread_timer);
pthread_join(thread_timer, NULL); //wait the thread stopped
}
//////////////////////////privatemethods//////////////////////////
voidCTimer::thread_proc(){
while (true) {
OnTimer(); ///问题变成,OnTimer()-last-end至OnTimer-next-start的时间间隔
///为tempval。期望的是OnTimer()-last-start至OnTimer-next-start
///的时间间隔为tempval,如此必然要使用线程间通信。
/* 加锁、解锁的时间消耗对该系统而言不足挂齿。可以用锁实现:
添加成员:QMutex mTimerLock;
void waitToStart(){
mTimerLock.lock();
}
bool isReadyToStart(){
return mTimerLock.trylock();
}
修改thread_proc()函数,睡眠结束后只解锁mTimerLock,继续休眠。
只是再注意一下回调函数处理时间比定时时间长的情况。
*/
pthread_testcancel();
structtimeval tempval;
tempval.tv_sec= m_second;
tempval.tv_usec= m_microsecond;
select(0,NULL, NULL, NULL, &tempval);
}
}
void CTimer::OnTimer(){
cout<<"Timer once..."<
}
structtimeval tempval;
tempval.tv_sec= m_second;
tempval.tv_usec= m_microsecond;
select(0,NULL, NULL, NULL, &tempval);
}
}
void CTimer::OnTimer(){
cout<<"Timer once..."<
(三)老老实实用QThread、QTimer封装吧
Qt中类在哪个线程中实例化,其槽函数的运行空间就在该线程。
qDebug() << "Main thread id: " <<gettid() <<"\n";
mThrCapture = new QThread(0);
mTmrCapture = new QTimer(0);
TimerHandler *tmrHandler =new TimerHandler(0);
tmrHandler->moveToThread(mThrCapture);
mTmrCapture->moveToThread(mThrCapture);
mTmrCapture->setInterval(3000);
//这两个槽函数都是打印线程ID
connect(mTmrCapture,SIGNAL(timeout()), this,SLOT(sltTmrCapture())); ///this指向的对象在主线程中创建,
///所以该函数在主线程空间中执行。
connect(mTmrCapture,SIGNAL(timeout()), tmrHandler,SLOT(sltTmrGreek())); ///tmrHandler被moveToThread至子线程,
///故该函数在子线程空间中执行。
connect(mThrCapture,SIGNAL(started()), mTmrCapture,SLOT(start()));
mThrCapture->start();
结果如下:
先就这么办吧。
Qt中类在哪个线程中实例化,其槽函数的运行空间就在该线程。
qDebug() << "Main thread id: " <<gettid() <<"\n";
mThrCapture = new QThread(0);
mTmrCapture = new QTimer(0);
TimerHandler *tmrHandler =new TimerHandler(0);
tmrHandler->moveToThread(mThrCapture);
mTmrCapture->moveToThread(mThrCapture);
mTmrCapture->setInterval(3000);
//这两个槽函数都是打印线程ID
connect(mTmrCapture,SIGNAL(timeout()), this,SLOT(sltTmrCapture())); ///this指向的对象在主线程中创建,
///所以该函数在主线程空间中执行。
connect(mTmrCapture,SIGNAL(timeout()), tmrHandler,SLOT(sltTmrGreek())); ///tmrHandler被moveToThread至子线程,
///故该函数在子线程空间中执行。
connect(mThrCapture,SIGNAL(started()), mTmrCapture,SLOT(start()));
mThrCapture->start();
结果如下:
Main threadid: 6259
6260
先就这么办吧。