最近項目中使用了PowerJob做任務調度模塊,感覺這個框架真香,今天我們就來深入瞭解一下新一代的定時任務框架——PowerJob! 簡介 PowerJob是基於java開發的企業級的分散式任務調度平臺,與xxl-job一樣,基於web頁面實現任務調度配置與記錄,使用簡單,上手快速,其主要功能特性如 ...
要點回顧
- 繼承自
std::enable_shared_from_this<T>
的類能夠在其自身實例中通過std::shared_from_this
方法創建一個指向自己的std::shared_ptr<T>
智能指針。 - 從一個裸指針創建多個
std::shared_ptr<T>
實例會造成嚴重的後果,其行為是未定義的。 std::enable_shared_from_this<T>
實際包含了一個用於指向對象自身的std::weak_ptr<T>
指針。
引言
本文介紹 std::enable_shared_from_this
及 std::shared_from_this
的基本概念和使用方法。
定義 "std::enable_shared_from_this"
以下內容是 cppreference.com 上關於 std::enable_shared_from_this
的定義和說明:
Defined in header < memory >
template< class T > class enable_shared_from_this; (since C++11)
std::enable_shared_from_this
allows an object t that is currently managed by astd::shared_ptr
named ptto safely generate additional std::shared_ptr instances
pt1, pt2, ... that all share ownership of t with pt.Publicly inheriting from
std::enable_shared_from_this<T>
provides the type T with a member functionshared_from_this
. If an object t of type T is managed by astd::shared_ptr<T>
named pt, then callingT::shared_from_this
will return a newstd::shared_ptr<T>
that shares ownership of t with pt.
簡單來說就是,繼承自 std::enable_shared_from_this<T>
的類能夠在其自身實例中通過 std::shared_from_this
方法創建一個指向自己的 std::shared_ptr<T>
智能指針。
想要理解 std::enable_shared_from_this<T>
,首先得知道為什麼需要 std::enable_shared_from_this<T>
,請看下文。
使用 "std::enable_shared_from_this"
為什麼需要 std::enable_shared_from_this<T>
? 我們從一個例子講起,會更容易一些。
假定有一個類 Processor, 它的作用是非同步處理數據並且存儲到資料庫。當 Processor 接收到數據時,它通過一個定製的 Executor 類型來非同步處理數據:
class Executor {
public:
//Executes a task asynchronously
void
execute(const std::function<void(void)>& task);
//....
private:
/* Contains threads and a task queue */
};
class Processor {
public:
//...
//Processes data asynchronously. (implemented later)
void processData(const std::string& data);
private:
//Called in an Executor thread
void doProcessAndSave(const std::string& data) {
//1. Process data
//2. Save data to DB
}
//.. more fields including a DB handle..
Executor* executor;
};
Client 類包含了一個 std::shared_ptr<Processor>
實例,Processor 從 Client 類接收數據:
class Client {
public:
void someMethod() {
//...
processor->processData("Some Data");
//..do something else
}
private:
std::shared_ptr<Processor> processor;
};
以上示例中,Executor
是一個線程池,用於執行任務隊列中的任務。
在 Processor::processData
中,我們需要傳遞一個(指針)函數(lambda 函數)給 Executor
來執行非同步操作。該 lambda 函數調用 Processor::doProcessAndSave
以完成實際的數據處理工作。因此,該 lambda 函數需要捕獲一個 Processor 對象的引用/指針。我們可以這樣做:
void Processor::processData(const std::string& data) {
executor->execute([this, data]() { //<--Bad Idea
//Executes in an Executor thread asynchronously
//'this' could be invalid here.
doProcessAndSave(data);
});
}
然而,因為種種原因,Client 可能會隨時重置 std::shared_ptr<Processor>
,這可能導致 Processor 的實例被析構。因此,在執行 execute 函數時或者在執行之前,上述代碼中捕獲的 this 指針隨時可能會變為無效指針。
怎麼辦?
我們可以通過在 lambda 函數中捕獲並保留一個指向當前對象本身的 std::shared_ptr<Processor>
實例來防止 Processor 對象被析構。
下圖展示了示例代碼的交互邏輯:
那麼,在 Processor 實例中通過 shared_ptr
std::shared_ptr<T>
允許我們安全地訪問和管理對象的生命周期。多個 std::shared_ptr<T>
實例通過一個共用控制塊結構(a shared control block structure)來管理對象的生命周期。一個控制塊維護了一個引用計數,及其他必要的對象本身的信息。
void good() {
auto p{new int(10)}; //p is int*
std::shared_ptr<int> sp1{p};
//Create additional shared_ptr from an existing shared_ptr
auto sp2{sp1}; //OK. sp2 shares control block with sp1
}
從一個裸指針創建一個 std::shared_ptr<T>
會創建一個新的控制塊。從一個裸指針創建多個 std::shared_ptr<T>
實例會造成嚴重的後果:
void bad() {
auto p{new int(10)};
std::shared_ptr<int> sp1{p};
std::shared_ptr<int> sp2{p}; //! Undefined Behavior
}
因此,我們需要一個機制能夠達到我們的目的(捕獲並保留一個指向當前對象本身的 std::shared_ptr<Processor>
實例)。
這就是 std::enable_shared_from_this<T>
存在的意義,以下是修改後的類 Processor 實現:
class Processor : public std::enable_shared_from_this<Processor> {
//..same as above...
};
void Processor::processData(const std::string& data) {
//shared_from_this() returns a shared_ptr<Processor>
// to this Processor
executor->execute([self = shared_from_this(), data]() {
//Executes in an Executor thread asynchronously
//'self' is captured shared_ptr<Processor>
self->doProcessAndSave(data); //OK.
});
}
self = shared_from_this()
傳遞的是一個合法的 std::shared_ptr<Processor>
實例,合法的類 Processor 對象的引用。
深入 "std::enable_shared_from_this" 內部
std::enable_shared_from_this<T>
的實現類似:
template<class T>
class enable_shared_from_this {
mutable weak_ptr<T> weak_this;
public:
shared_ptr<T> shared_from_this() {
return shared_ptr<T>(weak_this);
}
//const overload
shared_ptr<const T> shared_from_this() const {
return shared_ptr<const T>(weak_this);
}
//..more methods and constructors..
//there is weak_from_this() also since C++17
template <class U> friend class shared_ptr;
};
enable_shared_from_this
包含了一個 std::weak_ptr<T>
指針,這正是函數 shared_from_this
返回的內容。註意,為什麼不是 std::shared_ptr<T>
? 因為對象包含自身的計數引用會導致對象永遠不被釋放,從而發生記憶體泄漏。上述代碼中 weak_this
會在類對象被 std::shared_ptr<T>
引用時賦值,也就是std::shared_ptr<T>
實例的構造函數中賦值,這也是為什麼類 enable_shared_from_this
的最後,其被聲明成為了 shared_ptr
的友元。
總結
到此,關於 std::enable_shared_from_this<T>
的介紹就結束了。
引用
https://en.cppreference.com/w/cpp/memory/enable_shared_from_this
https://www.nextptr.com/tutorial/ta1414193955/enable_shared_from_this-overview-examples-and-internals