由GH-MD-TOC創建
Soboctizer是C ++的少數幾個跨平台和OpenSOURCE的“ Actor Frameworks”之一。但是Soboctiber不僅支持Actor模型,還支持發布訂閱模型和類似CSP的頻道。 Soboctizer的目標是對C ++中並發和多線程應用程序的開發進行了重大簡化。
Sobjectizer允許創建一個並發應用程序作為一組代理 - 對象,它們通過異步消息相互交互。它處理派遣消息並提供了用於消息處理的工作環境。並允許通過提供各種現成的調度員來調整這些內容。
成熟。 Soboctizer基於1995 - 2000年提出的想法。自2002年以來,正在開發Soboctizer本身。自2010年以來,Soboctizer-5一直不斷發展。
穩定。從一開始,Soboctizer用於業務關鍵應用,其中一些仍在生產中使用。 sobjectizer的破壞變化很少見,我們非常仔細地對待它們。
跨平台。 Soboctizer在Windows,Linux,FreeBSD,MacOS和Android上運行。
便於使用。 Soboctizer提供了易於理解且易於使用的API,並在Sobjectizer的分佈式中提供了許多示例,並且在項目的Wiki中提供了很多信息。
自由的。 Soboctizer根據BSD-3-CAREASE許可分發,因此可以免費開發專有商業軟件。
經常將Soboctizer與Intel螺紋構建塊,任務流,HPX等工具進行比較。這種比較只是毫無用處。
所有這些工具旨在用於從並行計算領域求解任務:它們可以通過使用多個CPU內核來減少計算時間。例如,您可以在一個CPU核心的一小時內將視頻文件從一種格式重新編碼到另一種格式,在四個內核上僅需15分鐘。這是並行計算的主要目標。
Soboctizer適用於略有不同的區域:並發計算。 Soboctizer的主要目標是簡化一次執行許多不同的任務。有時,不需要使用不僅僅是一個CPU核心。但是,如果有幾個CPU內核,那麼Soboctizer會使處理這些任務以及它們之間的交互作用更加容易。
棘手的部分是一個事實,即並發計算使用相同的並發機制和原始詞(例如遮光罩下的線,靜音,原子等)。但是,從高級角度來看,並行計算將其用於非常不同的任務。
作為可以或可以在Sobjectizer頂部實現的應用程序的示例,我們可以列出多線程代理服務器,自動控制系統,MQ-BROKR,數據庫服務器等。
這是一個經典的示例“ Hello,World”,通過使用Soboctizer的代理表示:
# include < so_5/all.hpp >
class hello_actor final : public so_5:: agent_t {
public:
using so_5:: agent_t :: agent_t ;
void so_evt_start () override {
std::cout << " Hello, World! " << std::endl;
// Finish work of example.
so_deregister_agent_coop_normally ();
}
};
int main () {
// Launch SObjectizer.
so_5::launch ([](so_5:: environment_t & env) {
// Add a hello_actor instance in a new cooperation.
env. register_agent_as_coop ( env. make_agent <hello_actor>() );
});
return 0 ;
}讓我們來看看有兩個代理商的更有趣的示例,並在它們之間進行消息交流。這是演員框架“乒乓球”的另一個著名例子:
# include < so_5/all.hpp >
struct ping {
int counter_;
};
struct pong {
int counter_;
};
class pinger final : public so_5:: agent_t {
so_5:: mbox_t ponger_;
void on_pong ( mhood_t <pong> cmd) {
if (cmd-> counter_ > 0 )
so_5::send<ping>(ponger_, cmd-> counter_ - 1 );
else
so_deregister_agent_coop_normally ();
}
public:
pinger ( context_t ctx) : so_5:: agent_t { std::move (ctx)} {}
void set_ponger ( const so_5:: mbox_t mbox) { ponger_ = mbox; }
void so_define_agent () override {
so_subscribe_self (). event ( &pinger::on_pong );
}
void so_evt_start () override {
so_5::send<ping>(ponger_, 1000 );
}
};
class ponger final : public so_5:: agent_t {
const so_5:: mbox_t pinger_;
int pings_received_{};
public:
ponger ( context_t ctx, so_5:: mbox_t pinger)
: so_5:: agent_t { std::move (ctx)}
, pinger_{ std::move (pinger)}
{}
void so_define_agent () override {
so_subscribe_self (). event (
[ this ]( mhood_t <ping> cmd) {
++pings_received_;
so_5::send<pong>(pinger_, cmd-> counter_ );
});
}
void so_evt_finish () override {
std::cout << " pings received: " << pings_received_ << std::endl;
}
};
int main () {
so_5::launch ([](so_5:: environment_t & env) {
env. introduce_coop ([](so_5:: coop_t & coop) {
auto pinger_actor = coop. make_agent <pinger>();
auto ponger_actor = coop. make_agent <ponger>(
pinger_actor-> so_direct_mbox ());
pinger_actor-> set_ponger (ponger_actor-> so_direct_mbox ());
});
});
return 0 ;
}上面代碼中的所有代理都在同一工作線程上工作。如何將它們綁定到不同的工作線程?
這很簡單。只需使用適當的調度程序:
int main () {
so_5::launch ([](so_5:: environment_t & env) {
env. introduce_coop (
so_5::disp::active_obj::make_dispatcher (env). binder (),
[](so_5:: coop_t & coop) {
auto pinger_actor = coop. make_agent <pinger>();
auto ponger_actor = coop. make_agent <ponger>(
pinger_actor-> so_direct_mbox ());
pinger_actor-> set_ponger (ponger_actor-> so_direct_mbox ());
});
});
return 0 ;
}Sobjectizer通過多生產者/多消費者消息框來支持酒吧/子模型。該消息類型的所有訂戶將收到發送到該消息框的消息:
# include < so_5/all.hpp >
using namespace std ::literals ;
struct acquired_value {
std::chrono::steady_clock::time_point acquired_at_;
int value_;
};
class producer final : public so_5:: agent_t {
const so_5:: mbox_t board_;
so_5:: timer_id_t timer_;
int counter_{};
struct acquisition_time final : public so_5:: signal_t {};
void on_timer ( mhood_t <acquisition_time>) {
// Publish the next value for all consumers.
so_5::send<acquired_value>(
board_, std::chrono::steady_clock::now (), ++counter_);
}
public:
producer ( context_t ctx, so_5:: mbox_t board)
: so_5:: agent_t { std::move (ctx)}
, board_{ std::move (board)}
{}
void so_define_agent () override {
so_subscribe_self (). event (&producer::on_timer);
}
void so_evt_start () override {
// Agent will periodically recive acquisition_time signal
// without initial delay and with period of 750ms.
timer_ = so_5::send_periodic<acquisition_time>(* this , 0ms, 750ms);
}
};
class consumer final : public so_5:: agent_t {
const so_5:: mbox_t board_;
const std::string name_;
void on_value ( mhood_t <acquired_value> cmd) {
std::cout << name_ << " : " << cmd-> value_ << std::endl;
}
public:
consumer ( context_t ctx, so_5:: mbox_t board, std::string name)
: so_5:: agent_t { std::move (ctx)}
, board_{ std::move (board)}
, name_{ std::move (name)}
{}
void so_define_agent () override {
so_subscribe (board_). event (&consumer::on_value);
}
};
int main () {
so_5::launch ([](so_5:: environment_t & env) {
auto board = env. create_mbox ();
env. introduce_coop ([board](so_5:: coop_t & coop) {
coop. make_agent <producer>(board);
coop. make_agent <consumer>(board, " first " s);
coop. make_agent <consumer>(board, " second " s);
});
std::this_thread::sleep_for ( std::chrono::seconds ( 4 ));
env. stop ();
});
return 0 ;
}Soboctizer中的所有代理都是有限狀態的機器。幾乎支持分層有限態機器(HSM)的所有功能:兒童狀態和處理者繼承,on_enter/on_exit處理程序,狀態超時,深度和淺層狀態歷史,除了正交狀態。
讓我們看看實現以下statechart的代理如何看起來像:

這是一個非常簡單的示例,演示了上面顯示的statechart代理:
# include < so_5/all.hpp >
using namespace std ::literals ;
class blinking_led final : public so_5:: agent_t {
state_t off{ this }, blinking{ this },
blink_on{ initial_substate_of{ blinking } },
blink_off{ substate_of{ blinking } };
public :
struct turn_on_off : public so_5 :: signal_t {};
blinking_led ( context_t ctx) : so_5:: agent_t { std::move (ctx)} {
this >>= off;
off. just_switch_to <turn_on_off>(blinking);
blinking. just_switch_to <turn_on_off>(off);
blink_on
. on_enter ([]{ std::cout << " ON " << std::endl; })
. on_exit ([]{ std::cout << " off " << std::endl; })
. time_limit (1250ms, blink_off);
blink_off
. time_limit (750ms, blink_on);
}
};
int main ()
{
so_5::launch ([](so_5:: environment_t & env) {
so_5:: mbox_t m;
env. introduce_coop ([&](so_5:: coop_t & coop) {
auto led = coop. make_agent < blinking_led >();
m = led-> so_direct_mbox ();
});
const auto pause = []( auto duration) {
std::this_thread::sleep_for (duration);
};
std::cout << " Turn blinking on for 10s " << std::endl;
so_5::send<blinking_led::turn_on_off>(m);
pause (10s);
std::cout << " Turn blinking off for 5s " << std::endl;
so_5::send<blinking_led::turn_on_off>(m);
pause (5s);
std::cout << " Turn blinking on for 5s " << std::endl;
so_5::send<blinking_led::turn_on_off>(m);
pause (5s);
std::cout << " Stopping... " << std::endl;
env. stop ();
} );
return 0 ;
}Soboctizer允許編寫並發應用程序,即使沒有內部的代理。只能使用普通線和類似CSP的通道。
這是ping-pong示例的樸素線程實現(請注意,main()不是例外安全):
# include < so_5/all.hpp >
struct ping {
int counter_;
};
struct pong {
int counter_;
};
void pinger_proc (so_5:: mchain_t self_ch, so_5:: mchain_t ping_ch) {
so_5::send<ping>(ping_ch, 1000 );
// Read all message until channel will be closed.
so_5::receive ( so_5::from (self_ch). handle_all (),
[&](so_5:: mhood_t <pong> cmd) {
if (cmd-> counter_ > 0 )
so_5::send<ping>(ping_ch, cmd-> counter_ - 1 );
else {
// Channels have to be closed to break `receive` calls.
so_5::close_drop_content (so_5::exceptions_enabled, self_ch);
so_5::close_drop_content (so_5::exceptions_enabled, ping_ch);
}
});
}
void ponger_proc (so_5:: mchain_t self_ch, so_5:: mchain_t pong_ch) {
int pings_received{};
// Read all message until channel will be closed.
so_5::receive ( so_5::from (self_ch). handle_all (),
[&](so_5:: mhood_t <ping> cmd) {
++pings_received;
so_5::send<pong>(pong_ch, cmd-> counter_ );
});
std::cout << " pings received: " << pings_received << std::endl;
}
int main () {
so_5:: wrapped_env_t sobj;
auto pinger_ch = so_5::create_mchain (sobj);
auto ponger_ch = so_5::create_mchain (sobj);
std::thread pinger{pinger_proc, pinger_ch, ponger_ch};
std::thread ponger{ponger_proc, ponger_ch, pinger_ch};
ponger. join ();
pinger. join ();
return 0 ;
}SoBoctizer提供了類似於Golang的Select語句的Select()函數。此功能允許等待來自多個消息鏈的傳入消息。它還允許等待消息鏈的準備就緒,以接受新的傳出消息。因此,Select()允許在目標消息鏈滿足時使用傳入消息的處理進行非阻止send()調用。
有一個fibonacci計算示例,該示例使用select()作為後壓機制(如果數字讀取器線程尚未讀取上一個編號,則數字生產者線程將等待)。還要注意,在此示例中,main()函數是異常安全。
# include < so_5/all.hpp >
# include < chrono >
using namespace std ;
using namespace std ::chrono_literals ;
using namespace so_5 ;
struct quit {};
void fibonacci ( mchain_t values_ch, mchain_t quit_ch )
{
int x = 0 , y = 1 ;
mchain_select_result_t r;
do
{
r = select (
from_all (). handle_n ( 1 ),
// Sends a new message of type 'int' with value 'x' inside
// when values_ch is ready for a new outgoing message.
send_case ( values_ch, message_holder_t < int >:: make (x),
[&x, &y] { // This block of code will be called after the send().
auto old_x = x;
x = y; y = old_x + y;
} ),
// Receive a 'quit' message from quit_ch if it is here.
receive_case ( quit_ch, [](quit){} ) );
}
// Continue the loop while we send something and receive nothing.
while ( r. was_sent () && !r. was_handled () );
}
int main ()
{
wrapped_env_t sobj;
thread fibonacci_thr;
auto thr_joiner = auto_join ( fibonacci_thr );
// The chain for Fibonacci number will have limited capacity.
auto values_ch = create_mchain ( sobj, 1s, 1 ,
mchain_props:: memory_usage_t ::preallocated,
mchain_props:: overflow_reaction_t ::abort_app );
auto quit_ch = create_mchain ( sobj );
auto ch_closer = auto_close_drop_content ( values_ch, quit_ch );
fibonacci_thr = thread{ fibonacci, values_ch, quit_ch };
// Read the first 10 numbers from values_ch.
receive ( from ( values_ch ). handle_n ( 10 ),
// And show every number to the standard output.
[]( int v ) { cout << v << endl; } );
send< quit >( quit_ch );
}有關Soboctiber的更多信息,請參見項目Wiki的相應部分。
有一個單獨的伴侶項目SO5EXTRA包含許多有用的內容,例如基於ASIO的調度員,其他類型的Mbox,可撤銷的計時器,同步請求等。
例如,有同步互動的樣子(通過使用so_5::extra::sync stuck):
# include < so_5_extra/sync/pub.hpp >
# include < so_5/all.hpp >
// Short alias for convenience.
namespace sync_ns = so_5::extra::sync;
using namespace std ::chrono_literals ;
// The type of service provider.
class service_provider_t final : public so_5:: agent_t
{
public :
using so_5:: agent_t :: agent_t ;
void so_define_agent () override
{
so_subscribe_self (). event (
[]( sync_ns:: request_mhood_t < int , std::string> cmd ) {
// Transform the incoming value, convert the result
// to string and send the resulting string back.
cmd-> make_reply ( std::to_string (cmd-> request () * 2 ) );
} );
}
};
// The type of service consumer.
class consumer_t final : public so_5:: agent_t
{
// Message box of the service provider.
const so_5:: mbox_t m_service;
public :
consumer_t ( context_t ctx, so_5:: mbox_t service )
: so_5:: agent_t { std::move (ctx) }
, m_service{ std::move (service) }
{}
void so_evt_start () override
{
// Issue a request and wait for the result no more than 500ms.
auto result = sync_ns::request_reply< int , std::string>(
// The destination for the request.
m_service,
// Max waiting time.
500ms,
// Request's value.
4 );
std::cout << " The result: " << result << std::endl;
so_deregister_agent_coop_normally ();
}
};
int main ()
{
so_5::launch ( [](so_5:: environment_t & env) {
env. introduce_coop (
// Every agent should work on its own thread.
so_5::disp::active_obj::make_dispatcher ( env ). binder (),
[](so_5:: coop_t & coop) {
auto service_mbox = coop. make_agent < service_provider_t >()
-> so_direct_mbox ();
coop. make_agent < consumer_t >( service_mbox );
} );
} );
}Soboctiber本身旨在是一個相對較小的項目,而沒有外部依賴性。 SO5Extra沒有此約束。這就是為什麼在SO5EXTRA中實現了基於ASIO的調度程序和環境基礎架構,而不是在Sobjectizer中實現。
Soboctizer的另一個重要特性是穩定性。我們正在努力使Soboctizer盡可能穩定,但是有必要嘗試一些新功能,即使我們還不知道它們會多麼成功和要求。 SO5Extra是嘗試新功能的好地方,其中一些可以隨著時間的流逝而移至Soboctizer。
因此,如果您在Soboctizer中找不到有用的功能,那麼讓我們嘗試查看SO5Extra。也許已經在那裡。
Soboctizer是一個進程的消息派遣框架。它不僅僅支持分佈式應用程序。但是在這種情況下可以使用外部工具和庫。請看一下我們的Mosquitto_transport實驗:https://github.com/stiffstream/mosquitto_transport
可以從GitHub檢查Soboctizer。具有Soboctizer源代碼的檔案可以從GitHub或SourceForge下載。
建造Soboctizer的方法有兩種。使用MXX_RU工具的第一個。第二個使用Cmake。
筆記。自從V.5.5.15.2自android平台提供支持。僅通過CMake可以為Android建造。請參閱下面的相應部分。
也可以通過VCPKG和CONAN依賴管理者安裝和使用Soboctizer。請參閱下面的適當部分。
5.8支sobjectizer需要C ++ 17。
如果您需要對C ++ 14或C ++ 11的支持,請嘗試在SourceForge上尋找舊版本的Sobjectizer。或接觸僵局,討論Soboctizer-5.8的移植到較舊的C ++標準。
筆記。這是構建Soboctizer的標準方法。這種方式用於Soboctizer開發過程。
要構建Soboctizer,有必要使用Ruby語言和MXX_RU工具。安裝Ruby,然後通過RubyGems命令安裝MXX_RU:
gem install Mxx_ru如果您已經安裝了MXX_RU,請至少更新到1.6.14.6版本:
gem update Mxx_ru可以從github上的git存儲庫中獲得Soboctizer:
git clone https://github.com/stiffstream/sobjectizer構建Soboctizer:
cd sobjectizer/dev
ruby build.rb將構建用於Soboctizer的靜態和共享庫。庫將被放置在目標/釋放子目錄中。
如果您想構建共享庫:
cd sobjectizer/dev
ruby so_5/prj.rb或者,如果您想構建靜態庫:
cd sobjectizer/dev
ruby so_5/prj_s.rb用所有測試和样本構建Soboctizer:
cd sobjectizer/dev
ruby build_all.rb請注意,在FreeBSD下,有必要定義LD_LIBRARY_PATH環境變量。 FreeBSD下的實際構建命令順序可能如下:
cd sobjectizer/dev
export LD_LIBRARY_PATH=target/release
ruby build_all.rb為了構建Soboctizer的HTML格式文檔,必須使用Doxygen工具。如果安裝了:
cd sobjectizer/doxygen
doxygen生成的HTML文件將位於Soboctizer/dev/doc/html中。
筆記。如果您不通過自己指定mxx_ru_cpp_toolset,則MXX_RU將嘗試自動檢測C ++工具集。如果要使用系統中不默認的C ++編譯器,請手動定義MXX_RU_CPP_Toolset環境。看起來像:
export MXX_RU_CPP_TOOLSET= " clang_linux compiler_name=clang++-6 linker_name=clang++-6 "有關您需要在相應的文檔中找到的有關您需要的MXX_RU的更多信息。
要通過Cmake構建Soboctizer,有必要擁有Cmake以及如何使用它的知識。以下動作只是一個演示。有關Soboctizer的CMAKE構建系統的更多詳細信息,請參見DEV/CMAKE/CMAKEQUICKHOWTO.TXT
在命令行中獲取和構建Linux/freeBSD下的Soboctizer:
git clone https://github.com/stiffstream/sobjectizer
cd sobjectizer
mkdir cmake_build
cd cmake_build
cmake -DCMAKE_INSTALL_PREFIX=target -DCMAKE_BUILD_TYPE=Release ../dev
cmake --build . --config Release
cmake --build . --config Release --target install這些命令將創建所有必要的makefile,然後構建Soboctizer。如果需要構建示例和測試,請使用
cmake -DBUILD_ALL=ON -DCMAKE_INSTALL_PREFIX=target ../dev當“ make install”完成“完成”時./target'將包含兩個子文件夾。
Cmake Build System當前支持此選項:
SOBJECTIZER_BUILD_STATIC 。啟用建築Soboctizer作為靜態庫[默認:ON]SOBJECTIZER_BUILD_SHARED 。啟用構建Sobjectizer作為共享庫[默認:ON]BUILD_ALL 。啟用建築物示例和測試[默認:關閉]BUILD_EXAMPLES 。啟用建築物示例[默認:關閉]BUILD_TESTS 。啟用建築測試[默認值:關閉]請注意,如果打開BUILD_ALL或BUILD_EXAMPLES或BUILD_TESTS ,則必須打開SOBJECTIZER_BUILD_STATIC和SOBJECTIZER_BUILD_SHARED 。這意味著,如果關閉SOBJECTIZER_BUILD_STATIC或SOBJECTIZER_BUILD_SHARED ,則必須關閉BUILD_ALL / BUILD_EXAMPLES / BUILD_TESTS 。
從命令行中構建MS Visual Studio 2013的Windows soboctizer:
git clone https://github.com/stiffstream/sobjectizer
cd sobjectizer
mkdir cmake_build
cd cmake_build
cmake -DCMAKE_INSTALL_PREFIX=target -DCMAKE_BUILD_TYPE=Release -G " Visual Studio 15 2017 " ../dev
cmake --build . --config Release
cmake --build . --config Release --target install如果需要構建示例,請在CMAKE調用中使用BUILD_ALL :
cmake -DCMAKE_INSTALL_PREFIX=target -DCMAKE_BUILD_TYPE=Release -DBUILD_ALL=ON -G " Visual Studio 15 2017 " ../dev由於v.5.5.5.24 sobjectizer提供soboctizer-config.cmake文件。這些文件自動安裝到<target>/lib/cmake/sobjectizer子文件夾中。它允許通過CMAKE的FIND_PACKAGE命令使用Soboctizer。
通過相當新鮮的Android NDK或Crystax NDK可以為Android建造。
您需要係統中安裝的Android SDK和Android NDK。以及適當的CMAKE版本。您還需要正確設置環境變量ANDROID_HOME , ANDROID_NDK 。然後,您可以發出以下命令:
git clone https://github.com/stiffstream/sobjectizer
cd sobjectizer
mkdir cmake_build
cd cmake_build
cmake -DBUILD_ALL -DCMAKE_INSTALL_PREFIX=target -DCMAKE_BUILD_TYPE=Release
-DCMAKE_TOOLCHAIN_FILE= ${ANDROID_NDK} /build/cmake/android.toolchain.cmake
-G Ninja
-DANDROID_ABI=arm64-v8a
-DANDROID_NDK= ${ANDROID_NDK}
-DANDROID_NATIVE_API_LEVEL=23
-DANDROID_TOOLCHAIN=clang
../dev
cmake --build . --config=Release
cmake --build . --config=Release --target install您需要係統中已經安裝的Crystax NDK v.10.4.0或更高版本。 Cmake用於構建Soboctizer:
git clone https://github.com/stiffstream/sobjectizer
cd sobjectizer
mkdir cmake_build
cd cmake_build
export NDK=/path/to/the/crystax-ndk
cmake -DBUILD_ALL -DCMAKE_INSTALL_PREFIX=result -DCMAKE_TOOLCHAIN_FILE= $NDK /cmake/toolchain.cmake -DANDROID_ABI=arm64-v8a ../dev
make
make test
make install要通過VCPKG使用Soboctizer,必須執行以下步驟。
安裝sobjectizer軟件包:
vcpkg install sobjectizer將以下行添加到您的cmakelists.txt文件中:
find_package (sobjectizer CONFIG REQUIRED)
target_link_libraries (your_target sobjectizer::SharedLib) # or sobjectizer::StaticLib筆記。自2021年2月以來,新版本的Soboctizer僅可通過Conan-Center獲得。
要通過Conan使用Soboctizer,必須將soboctizer添加到項目的conanfile.txt :
[requires]
sobjectizer/5.8.0
也可能有必要為Sobjectizer指定shared選項。例如,對於構建Soboctizer作為靜態庫:
[options]
sobjectizer:shared=False
為您的項目安裝依賴項:
conan install SOME_PATH --build=missing
...
include ( ${CMAKE_BINARY_DIR} /conanbuildinfo.cmake)
conan_basic_setup()
...
target_link_libraries (your_target ${CONAN_LIBS} )Soboctizer根據BSD BSD許可證分配。有關許可信息,請參閱許可證文件。