最近研究了一下STL的thread庫,做爲學習成果,寫了幾個封裝類,把thread封裝起來,提供了sleep和stop方法,做爲對thread的補充。
基類是AbstractThread,定義了start、stop、sleep三個公開的虛函數。start方法用於啓動線程代碼運行,stop方法用於終止線程代碼,sleep方法供AbstractThread的子類使用,可用來暫停一段指定的時間。
AbstractThread還定義了三個保護接口虛函數:run、doStart、doIt、doStop。run函數是線程代碼的外層封裝,做一些初始化和收尾的工作,並調用doIt函數來執行線程代碼。doStart是虛函數,被start調用,AbstractThread的子類可以重載該函數,以執行線程開始運行之前的一些準備工作。doIt函數是純虛函數,AbstractThread的子類必須重載該函數,裏面是線程的實際業務代碼。doStop函數是虛函數,被stop函數調用,AbstractThread的子類可以重載該函數,以執行線程停止執行前的一些收尾工作。
AbstractThread的類定義代碼(absthread.h)如下:
#ifndef __ABSTHREAD_H
#define __ABSTHREAD_H
#include <atomic>
class AbstractThread
{
public:
AbstractThread();
virtual ~AbstractThread();
public:
virtual void start();
virtual void stop(bool shouldWait = true);
virtual void sleep(unsigned long millisec);
protected:
virtual void run();
virtual void doIt() = 0;
virtual void doStart();
virtual void doStop();
protected:
std::atomic_bool waking;
std::atomic_bool running;
};
#endif /* __ABSTHREAD_H */
AbstractThread類的實現代碼(absthread.cpp)如下:
#include <thread>
#include <chrono>
#include "absthread.h"
using namespace std;
using namespace chrono;
AbstractThread::AbstractThread()
{
waking = true;
running = false;
}
AbstractThread::~AbstractThread()
{
}
void AbstractThread::start()
{
waking = true;
running = true;
doStart();
thread t(this->run, this);
t.detach();
}
void AbstractThread::stop(bool shouldWait)
{
waking = true; // wake up the thread calling sleep(...).
doStop();
while(shouldWait && running);
}
void AbstractThread::sleep(unsigned long millisec)
{
system_clock::time_point start, stop;
chrono::milliseconds interval(millisec);
waking = false;
start = system_clock::now();
do {
stop = system_clock::now();
} while(((stop - start) < interval) && !waking);
}
void AbstractThread::doStart()
{
}
void AbstractThread::run()
{
running = true;
doIt();
running = false;
}
void AbstractThread::doStop()
{
}
AbstractThread類不能直接實例化,必須派生子類,子類至少要重載doIt方法,以執行線程業務代碼。
在AbstractThread基礎上寫了一個子類PeriodicThread,這個類實現了週期性任務線程,可以設定任務的重複週期,單位爲毫秒(ms)。PeriodicThread重寫了doStart、doStop和run方法,並額外提供了一個純虛函數接口doTask,用於執行需要重複執行的線程任務。
PeriodicThread的類聲明代碼(prdthread.h)如下所示:
#ifndef __PRDTHREAD_H
#define __PRDTHREAD_H
#include "absthread.h"
class PeriodicThread: public AbstractThread
{
public:
PeriodicThread(unsigned long period);
virtual ~PeriodicThread();
protected:
virtual void doIt();
virtual void doStart();
virtual void doStop();
virtual void doTask() = 0; // the derived class should override this method.
protected:
unsigned long period; // milli-seconds
std::atomic_bool shouldStop;
};
#endif /* __PRDTHREAD_H */
PeriodicThread類的實現代碼(prdthread.cpp)如下所示:
#include <chrono>
#include "prdthread.h"
using namespace std;
using namespace chrono;
PeriodicThread::PeriodicThread(unsigned long period)
{
this->period = period;
this->shouldStop = true;
}
PeriodicThread::~PeriodicThread()
{
}
void PeriodicThread::doIt()
{
milliseconds interval(period);
system_clock::time_point startTime, stopTime;
do {
doTask();
// wait 'period' ms
startTime = system_clock::now();
do {
stopTime = system_clock::now();
} while((!shouldStop) && ((stopTime - startTime) < interval));
} while(!shouldStop);
}
void PeriodicThread::doStart()
{
shouldStop = false;
}
void PeriodicThread::doStop()
{
shouldStop = true;
}
PeriodicThread的週期定時使用了chrono庫。
PeriodicThread類不能被實例化,必須派生子類,子類必須重載doTask方法,在其中執行需要重複運行的業務代碼。
最後是測試代碼(main.cpp),分別製作了AbstractThread的派生類LongtimeThread和PeriodicThread的派生類LoopThread。代碼如下:
#include <iostream>
#include <string>
#include <memory>
#include "absthread.h"
#include "prdthread.h"
using namespace std;
class LoopThread : public PeriodicThread
{
public:
LoopThread(unsigned long period) : PeriodicThread(period)
{
count = 0;
}
virtual ~LoopThread() {}
protected:
void doTask()
{
cout << "periodic thread running " << count ++ << " loops." << endl;
}
protected:
unsigned int count;
};
class LongtimeThread : public AbstractThread
{
public:
LongtimeThread(unsigned long sleepInterval)
{
this->sleepInterval = sleepInterval;
this->shouldStop = true;
}
virtual ~LongtimeThread() {}
protected:
virtual void doIt()
{
shouldStop = false;
for(int count = 0; count < 10; count ++)
{
cout << "longtime thread running " << count << " loops." << endl;
sleep(sleepInterval);
if (shouldStop)
{
break;
}
}
}
virtual void doStop()
{
shouldStop = true;
}
protected:
unsigned long sleepInterval;
atomic_bool shouldStop;
};
int main()
{
string command;
unique_ptr<LoopThread> lt(new LoopThread(2000));
unique_ptr<LongtimeThread> ltt(new LongtimeThread(3000));
lt->start();
ltt->start();
do {
cout << ">>> input command (\"exit\" to exit): " << endl;
cin >> command;
} while(command.compare("exit") != 0);
ltt->stop(true);
lt->stop(true);
return 0;
}
程序運行後,會提示用戶在鍵盤上輸入字符串(命令),在等待用戶輸入的同時,兩個線程也在同時執行,屏幕上會交錯出現兩個線程的運行時輸出。如果用戶在鍵盤上輸入字符串"exit",則程序中止兩個線程,並結束運行。
運行時顯示的信息類似以下內容:
>>> input command ("exit" to exit):
periodic thread running 0 loops.
longtime thread running 0 loops.
periodic thread running 1 loops.
longtime thread running 1 loops.
helloperiodic thread running 2 loops.
>>> input command ("exit" to exit):
periodic thread running 3 loops.
longtime thread running 2 loops.
stop
>>> input command ("exit" to exit):
periodic thread running 4 loops.
longtime thread running 3 loops.
exit
以上代碼在Eclipse CDT 9.10中調試通過。