Skip to content

Latest commit

 

History

History
167 lines (153 loc) · 6.15 KB

ThreadTools.md

File metadata and controls

167 lines (153 loc) · 6.15 KB

fon9 library - Thread Tools

fon9::AQueue

  • fon9/AQueue.hpp
  • 複雜操作集中在一個 queue 裡面依序處理。
  • 需要盡快(as soon as possible)執行的簡單操作(例如: 簡單的Get、Set), 可透過 lock 後檢查是否允許立即執行,如果不行則要求排入 queue 裡面依序處理。
  • 應另開專頁說明,目前在 fon9::io::Device 有用到。

fon9::MustLock

  • fon9/MustLock.hpp
  • 當某個物件需要提供 thread safe 特性時,一般而言都需要用 mutex 來保護,通常寫法如下, 但這樣的缺點非常明顯:每次要使用 obj_ 時,都要「記得」使用 lock,一旦有地方遺漏了,就會造成難以發現的 bug。
    • 宣告:
      std::mutex mx_;
      MyObject   obj_;
    • 使用:
      std::unique_lock lk(mx_);
      obj_.foo();
  • 所以 libfon9 設計了一個「強制要求」必須鎖住才能使用的機制:
    fon9::MustLock<BaseT,MutexT>
    • 宣告:
      using MyThreadSafeObj = fon9::MustLock<MyObject,std::mutex>;
      MyThreadSafeObj obj_;
    • 使用:
      MyThreadSafeObj::Locker obj{obj_}; // 或 auto obj{obj_.Lock()};
      obj->foo(); // lock 之後才能使用, 此時使用任何一個 MyObject 的 member 都是 thread safe.
    • 缺點(副作用):如果 MyObject::bar() 本身就已是 thread safe,不需要 lock,則仍必須 lock 才能使用。
      • 可以避免誤用「容易有錯誤認知的 member」,例如: std::string::empty() const 是 thread safe 嗎?

fon9::DummyMutex

SleepPolicy

fon9::SpinMutex<SleepPolicy>

WaitPolicy

  • fon9/WaitPolicy.hpp
  • fon9::WaitPolicy_CV
  • fon9::WaitPolicy_Spin<MutexT>
  • using SpinBusy = SpinMutex<BusySleepPolicy>;

fon9::CountDownLatch

fon9::CyclicBarrier

fon9::ThreadController<ProtectedT, WaitPolicy>

fon9::Worker<WorkContentController>

  • fon9/Worker.hpp
  • 控制一次只會有一個 thread 執行 Worker,但是允許任意 thread 要求 Worker 做事。

MessageQueue

template <
   class MessageHandlerT,
   class MessageT = typename MessageHandlerT::MessageType,
   class MessageContainerT = std::deque<MessageT>,
   class WaitPolicy = WaitPolicy_CV
>
class MessageQueue;

fon9::GetDefaultThreadPool()

  • fon9/DefaultThreadPool.hpp
  • 取得 fon9 提供的一個 thread pool.
    • 一般用於不急迫, 但比較花時間的簡單工作, 例如: 寫檔、domain name 查找...
    • 程式結束時, 剩餘的工作會被拋棄!

Timer 計時器

  • fon9/Timer.hpp
  • 由於 Timer 共用 TimerThread,所以若 OnTimer 事件的執行時間太久, 則會影響下一個 Timer,可能會超過預計的執行時間。

必須先建立一個讓 Timer 寄居的 thread: fon9::TimerThread

  • fon9::TimerThread& fon9::GetDefaultTimerThread(); 可取得 fon9 預設的 TimerThread

允許 MyObject 在觸發前死亡

使用 std::shared_ptr<MyObject> + fon9::TimerEntry_OwnerWP<MyObject, &MyObject::OnTimer> 觸發 MyObject::OnTimer() 事件

class MyObject : public std::enable_shared_from_this<MyObject> {
   void OnTimer(fon9::TimerEntry* timer, fon9::TimeStamp now) {
      // ... timer 觸發後, 執行這裡.
   }
   using Timer = fon9::TimerEntry_OwnerWP<MyObject, &MyObject::OnTimer>;
public:
   void RunAfter(fon9::TimerThread& timerThread, fon9::TimeInterval ti) {
      (new Timer{timerThread, this->shared_from_this()})->RunAfter(ti);
   }
};

在 MyObject 裡面包含一個 timer 物件, 然後觸發 static member function

class MyObject {
   fon9_NON_COPY_NON_MOVE(MyObject);
   static void EmitOnTimer(fon9::TimerEntry* timer, fon9::TimeStamp now) {
      MyObject& rthis = fon9::ContainerOf(*static_cast<decltype(MyObject::Timer_)*>(timer), &MyObject::Timer_);
      // 處理 Timer 事件...
   }
   fon9::DataMemberEmitOnTimer<&MyObject::EmitOnTimer> Timer_;
public:
   MyObject(fon9::TimerThread& timerThread) : Timer_(timerThread) {
   }
   ~MyObject() {
      // 必須在 MyObject 死亡前,確定 Timer_ 已不會再觸發,否則可能會 crash!
      this->Timer_.DisposeAndWait();
   }
   void RunAfter(fon9::TimeInterval delaySecs) {
      this->Timer_.RunAfter(delaySecs);
   }
};

在 MyObject 裡面包含一個 timer 物件

  • 繼承 fon9::DataMemberTimer
  • 覆寫 virtual void EmitOnTimer(fon9::TimeStamp now) override;
class MyObject {
   fon9_NON_COPY_NON_MOVE(MyObject);
   class Timer : public fon9::DataMemberTimer {
      fon9_NON_COPY_NON_MOVE(Timer);
      using base = fon9::DataMemberTimer;
      virtual void EmitOnTimer(fon9::TimeStamp now) override {
         MyObject& owner = fon9::ContainerOf(*this, &MyObject::Timer_);
         // ... Timer_ 觸發後, 執行這裡.
      }
   public:
      using base::base;
   };
   Timer   Timer_;
public:
   MyObject(fon9::TimerThread& timerThread) : Timer_(timerThread) {
   }
   ~MyObject() {
      // 必須在 MyObject 死亡前,確定 Timer_ 已不會再觸發,否則可能會 crash!
      this->Timer_.DisposeAndWait();
   }
   void RunAfter(fon9::TimeInterval delaySecs) {
      this->Timer_.RunAfter(delaySecs);
   }
};

fon9::SchTask

  • fon9/SchTask.hpp
  • 排程項目: 現在時間到達設定的範圍內觸發 InSch 事件, 離開設定的時間範圍觸發 OutSch 事件。