筆記:STL的thread庫應用

最近研究了一下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中調試通過。

發佈了54 篇原創文章 · 獲贊 10 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章