由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许可证分配。有关许可信息,请参阅许可证文件。