cornerstone中delayed_task,timer_task及scheduler原始碼解析

TomGeller發表於2024-11-20

1.概述

delayed_task在cornerstone中充當一個base類,其延伸出的子類timer_task才是cornerstone用於實現定時器的類。
而scheduler是用於實現延時效果的排程器。
我們將按照delayed_task->timer_task->scheduler的順序解析原始碼。

2.delayed_task原始碼解析

2.1 成員變數分析

class delayed_task
{
private:
    std::atomic<bool> cancelled_;
    void* impl_ctx_;
    std::function<void(void*)> impl_ctx_del_;
}
  • cancelled_表示是否取消了任務,使用atomic是防止在多執行緒環境下執行緒不安全。
  • impl_ctx_是給task具體執行的函式impl傳遞的上下文引數。
  • impl_ctx_del_負責刪除任務。
    這裡有3個知識點:
  • impl_ctx_設定成上下文引數,其是未知的,因此我們將其宣告為void*
  • 為了支援包括lambda在內的任意函式,將del函式宣告為std::function<void(void*)>
  • 由於task可能會被同時取消與呼叫,所以有多執行緒race-condition的情況

2.2 初始化

public:
    delayed_task() : cancelled_(false), impl_ctx_(nilptr), impl_ctx_del_()
    {
    }
    virtual ~delayed_task()
    {
        if (impl_ctx_ != nilptr)
        {
            if (impl_ctx_del_)
            {
                impl_ctx_del_(impl_ctx_);
            }
        }
    }

沒什麼好說的,就是對2.1成員分析的應用。

2.3 task執行

public:
    void execute()
    {
        if (!cancelled_.load())
        {
            exec();
        }
    }

execute是這個delayed_task對外提供的public介面,具體實現放在了exec裡面,實現介面與實現分離。

protected:
    virtual void exec() = 0;

宣告成virtual從而讓子類實現。
(即使宣告成private也可以讓子類實現,這是nvi(non-virtual-interface)技巧)

3.timer_task原始碼解析

template <typename T>
class timer_task : public delayed_task
{
using executor = std::function<void(T)>;
private:
    executor exec_;
    T ctx_;
}

首先看成員變數,exec_即為具體的執行函式,ctx_即為exec_所需要的上下文引數。

然後到執行

protected:
    virtual void exec() __override__
    {
        if (exec_)
        {
            exec_(ctx_);
        }
    }

這裡實現了基類宣告的exec,執行前判斷exec_是否有效。

4.scheduler原始碼解析

class delayed_task_scheduler
{
    __interface_body__(delayed_task_scheduler);

public:
    virtual void schedule(ptr<delayed_task>& task, int32 milliseconds) = 0;

    void cancel(ptr<delayed_task>& task)
    {
        cancel_impl(task);
        task->cancel();
    }

private:
    virtual void cancel_impl(ptr<delayed_task>& task) = 0;
};
}

schedule的任務就是負責將task延後milliseconds後執行。
同樣的這裡是宣告為純虛擬函式由子類實現。

子類的實現:

void asio_service::schedule(ptr<delayed_task>& task, int32 milliseconds)
{
    if (task->get_impl_context() == nilptr)
    {
        task->set_impl_context(new asio::steady_timer(impl_->io_svc_), &_free_timer_);
    }
    // ensure it's not in cancelled state
    task->reset();

    asio::steady_timer* timer = static_cast<asio::steady_timer*>(task->get_impl_context());
    timer->expires_after(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(milliseconds)));
    timer->async_wait([task](const asio::error_code err) mutable { _timer_handler_(task, err); });
}
void asio_service::cancel_impl(ptr<delayed_task>& task)
{
    if (task->get_impl_context() != nilptr)
    {
        static_cast<asio::steady_timer*>(task->get_impl_context())->cancel();
    }
}
  • 注意這裡scheduler傳入的引數是ptr<delayed_task>& task,但是在cornerstone裡面真正用於定時器的是timer_task,這裡存在多型的轉換。
  • 利用asio中的steady_timer,將task繫結到timer上面
  • timer->expires_after設定定時器到期時間,timer->async_wait非同步等待定時器到期,_timer_handler_(task, err);timer過期時呼叫回撥函式執行task從而實現schedule功能

5.總結

  • 1.使用atomic實現多執行緒無鎖程式設計。
  • 2.利用void*傳入任意型別引數,利用 std::function<void(void*)>支援包括lambda在內的任意函式。

相關文章