По следам темы eao197 слепил свою модель Ad-hoc легковесных недоакторов с помощью boost::asio::io_service и boost::signals2. Вот что получилось:
#include <iostream>
#include <mutex>
#include <thread>
#include <atomic>
#include <boost/asio/io_service.hpp>
#include <boost/signals2.hpp>
#define BOOST_TEST_MODULE Miscellaneous
#include <boost/test/unit_test.hpp>
namespace tests {
std::ostream&
print_one(std::ostream& os)
{
return os;
}
template <class A0, class... Args>
std::ostream&
print_one(std::ostream& os, const A0& a0, const Args&... args)
{
os << a0;
return print_one(os, args...);
}
template <class... Args>
std::ostream&
print(std::ostream& os, const Args&... args)
{
std::ostream& result = print_one(os, args...);
os.flush();
return result;
}
template <class... Args>
std::ostream&
print(const Args&... args)
{
static std::mutex m;
std::lock_guard<std::mutex> lg(m);
return print(std::cout, args...);
}
class BaseActor {
public:
BaseActor()
: thread_()
{
}
virtual ~BaseActor()
{
stop();
}
void start()
{
stop();
io_service.reset();
work_.reset(new boost::asio::io_service::work(io_service));
std::thread(std::bind(&BaseActor::threadFunc_, this)).swap(thread_);
}
void stop()
{
work_.reset();
io_service.stop();
if (thread_.joinable())
thread_.join();
}
public:
boost::asio::io_service io_service;
protected:
virtual void threadFunc_() { io_service.run(); };
protected:
std::thread thread_;
std::unique_ptr<boost::asio::io_service::work> work_;
};
class ActorA : public BaseActor {
public:
std::function<void(const int&)> getDoJobAInvoker()
{
return io_service.wrap(std::bind(&ActorA::doJobA_, this, std::placeholders::_1));
}
~ActorA() { stop(); };
public:
boost::signals2::signal<void(const std::string&)> helloSig;
std::atomic_int aNumber{ 0 };
protected:
virtual void threadFunc_()
{
print("ActorA has started", "\n");
io_service.run();
print("ActorA has finished", "\n");
};
private:
void doJobA_(const int& num)
{
print("ActorA::doJobA_() from thread ", thread_.get_id(), "\n");
print("num = ", num, "\n");
std::ostringstream oss;
oss << "Hello, World " << num << "!";
helloSig(oss.str());
aNumber.store(num);
}
};
class ActorB : public BaseActor {
public:
std::function<void(const std::string&)> getDoJobBInvoker()
{
return io_service.wrap(std::bind(&ActorB::doJobB_, this, std::placeholders::_1));
}
~ActorB() { stop(); };
public:
boost::signals2::signal<void(const int&)> intSig;
protected:
virtual void threadFunc_()
{
print("ActorB has started", "\n");
io_service.run();
print("ActorB has finished", "\n");
};
private:
void doJobB_(const std::string& str)
{
print("ActorB::doJobB_() from thread ", thread_.get_id(), "\n");
print("str = ", str, "\n");
intSig(++cout);
aString_ = str;
}
int cout = 0;
std::string aString_;
};
BOOST_AUTO_TEST_CASE(BoostIOServiceTest)
{
print("Main thread id is ", std::this_thread::get_id(), "\n");
ActorA actorA;
ActorB actorB;
actorA.helloSig.connect(actorB.getDoJobBInvoker());
actorB.intSig.connect(actorA.getDoJobAInvoker());
actorA.start();
actorB.start();
actorB.intSig(0);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
} // namespace tests
Этот код, разумеется, работает, и работает как положено, но есть у него недостаток: необходимо в деструкторах дочерних классов явно вызывать метод stop() базового класса, иначе велика вероятность, что при разрушении вызовется уже уничтоженный к этому времени сигнал, определенный в дочернем классе, а только потом остановится поток, который этот сигнал вызывает. Отсюда вопрос: как можно избавиться от необходимости явно вызывать BaseActor::stop() из деструкторов ActorA и ActorB?