Чуть больше года назад появился пост "Очередной CodeSize Battle: Just::Thread Pro vs SObjectizer-5.5.6". Поскольку версию 5.5.6 уже можно считать довольно старой, то имеет смысл привести для сравнения код, адаптированный под актуальный SO-5. Код для Just::Thread Pro не менялся, т.к. я не помню, чтобы Энтони Уильямс обновлял версию своего фреймворка.
Для тех, кто не хочет заглядывать в старый пост поясню, что речь идет от классической задаче с парикмахером, который спит, пока нет посетителей и просыпается, когда посетители появляются. Под катом показаны реализации на Just::Thread Pro и на SO-5.5.17.
Примечание. Реализация на SO-5 не 1-в-1, но очень близко соответствует версии Уильямса. Дабы не писать постоянно so_5::send(m_log,something) и не колупаться с обнулением содержимого std::ostringstream (что довольно часто происходит в версии Уильямса), в SObjectizer-овской версии было переопределено несколько вариантов operator<<=. Но, т.к. определение этих вспомогательных штук в коде присутствует и объем кода несколько раздувает, то это не такое уж и большое читтерство :)
#include <jss/actor.hpp>
#include <iostream>
#include <chrono>
#include <thread>
#include <stdlib.h>
#include <memory>
#include <sstream>
struct customer_waiting
{
jss::actor_ref customer;
};
struct customer_enters
{
jss::actor_ref customer;
};
struct customer_leaves
{};
struct start_haircut
{};
struct done_haircut
{};
struct closing_time
{};
struct no_room
{};
struct shop_closed
{};
struct no_customers
{};
void logger_func(){
for(;;){
jss::actor::receive()
.match<std::string>([](std::string s){
std::cout<<s<<std::endl;
});
}
}
jss::actor logger(logger_func);
void barber_func()
{
bool go_home=false;
unsigned haircuts=0;
while(!go_home)
{
logger<<std::string("barber is sleeping");
bool can_sleep=false;
do
{
jss::actor::receive()
.match<customer_waiting>(
[&](customer_waiting c){
logger<<std::string("barber is cutting hair");
c.customer<<start_haircut();
std::this_thread::sleep_for(std::chrono::milliseconds(1000*(1+(rand()%5))));
c.customer<<done_haircut();
++haircuts;
})
.match<no_customers>(
[&](no_customers){
can_sleep=true;
})
.match<closing_time>(
[&](closing_time){
go_home=true;
});
}
while(!can_sleep && !go_home);
}
std::ostringstream os;
os<<"barber is going home. He did "<<haircuts<<" haircuts today";
logger<<os.str();
}
void shop_func(jss::actor_ref barber)
{
bool closed=false;
unsigned waiting_customers=0;
unsigned const max_waiting_customers=3;
std::ostringstream os;
logger<<std::string("shop opens");
while(!closed)
{
jss::actor::receive()
.match<customer_enters>(
[&](customer_enters c){
++waiting_customers;
os.str("");
os<<"shop has "<<waiting_customers<<" customers";
logger<<os.str();
if(waiting_customers<=max_waiting_customers){
barber<<customer_waiting{c.customer};
} else
c.customer<<no_room();
})
.match<customer_leaves>(
[&](customer_leaves){
if(!--waiting_customers)
{
logger<<"last customer left shop";
barber<<no_customers();
} else{
os.str("");
os<<"shop has "<<waiting_customers<<" customers";
logger<<os.str();
}
})
.match<closing_time>(
[&](closing_time c){
logger<<std::string("shop closing");
closed=true;
barber<<c;
});
}
while(waiting_customers){
os.str("");
os<<"shop has "<<waiting_customers<<" customers";
logger<<os.str();
jss::actor::receive()
.match<customer_enters>(
[&](customer_enters c){
++waiting_customers;
logger<<"customer turned away because shop closed";
c.customer<<shop_closed();
})
.match<customer_leaves>(
[&](customer_leaves){
if(!--waiting_customers)
{
logger<<"last customer left shop";
}
});
}
logger<<std::string("shop closed");
}
enum class haircut_status{
had_haircut,no_room,shop_closed
};
haircut_status try_and_get_hair_cut(unsigned customer,jss::actor_ref shop){
std::ostringstream os;
os<<"customer "<<customer<<" goes into barber shop";
logger<<os.str();
try{
shop<<customer_enters{jss::actor::self()};
}
catch(jss::no_actor){
os.str("");
os<<"customer "<<customer<<" finds barber shop is closed";
logger<<os.str();
return haircut_status::shop_closed;
}
haircut_status status=haircut_status::no_room;
jss::actor::receive()
.match<start_haircut>(
[&](start_haircut)
{
os.str("");
os<<"customer "<<customer<<" is having a haircut";
logger<<os.str();
jss::actor::receive()
.match<done_haircut>(
[&](done_haircut)
{
os.str("");
os<<"customer "<<customer<<" is done having a haircut";
logger<<os.str();
}
);
status=haircut_status::had_haircut;
}
)
.match<no_room>(
[&](no_room)
{
os.str("");
os<<"customer "<<customer<<" leaves because there is no room";
logger<<os.str();
status=haircut_status::no_room;
}
)
.match<shop_closed>(
[&](shop_closed)
{
os.str("");
os<<"customer "<<customer<<" finds barber shop is closed";
logger<<os.str();
status=haircut_status::shop_closed;
}
);
os.str("");
os<<"customer "<<customer<<" leaves barber shop";
logger<<os.str();
try{
shop<<customer_leaves();
}
catch(jss::no_actor){
}
return status;
}
void customer_func(unsigned i,jss::actor_ref shop)
{
std::ostringstream os;
os<<"customer "<<i<<" goes to town";
logger<<os.str();
haircut_status status;
do{
os.str("");
os<<"customer "<<i<<" is shopping";
logger<<os.str();
std::this_thread::sleep_for(std::chrono::milliseconds(500*(rand()%20)));
}
while((status=try_and_get_hair_cut(i,shop))==haircut_status::no_room);
os.str("");
os<<"customer "<<i<<" is going home";
logger<<os.str();
}
int main()
{
{
jss::actor barber(barber_func);
jss::actor barbershop(shop_func,jss::actor_ref(barber));
unsigned const count=20;
jss::actor customers[count];
for(unsigned i=0;i<count;++i)
{
std::ostringstream os;
os<<"Starting customer "<<i;
logger<<os.str();
customers[i]=jss::actor(customer_func,i,jss::actor_ref(barbershop));
}
std::this_thread::sleep_for(std::chrono::seconds(20));
barbershop<<closing_time();
}
logger.stop();
}
|
|
#include <iostream>
#include <cstdlib>
#include <so_5/all.hpp>
using log_msg = std::string;
struct customer_waiting
{
so_5::mbox_t customer;
};
struct customer_enters
{
so_5::mbox_t customer;
};
struct customer_leaves : public so_5::signal_t {};
struct start_haircut : public so_5::signal_t {};
struct done_haircut : public so_5::signal_t {};
struct closing_time : public so_5::signal_t {};
struct no_room : public so_5::signal_t {};
struct shop_closed : public so_5::signal_t {};
struct no_customers : public so_5::signal_t {};
so_5::mbox_t
make_logger( so_5::coop_t & coop )
{
auto logger = coop.define_agent();
logger.event( logger, []( const log_msg & msg ) {
std::cout << msg << std::endl;
} );
return logger.direct_mbox();
}
template< typename A >
inline void operator<<=( const so_5::mbox_t & to, A && a )
{
so_5::send< log_msg >( to, std::forward< A >(a) );
}
struct msg_maker
{
std::ostringstream m_os;
template< typename A >
msg_maker & operator<<( A && a )
{
m_os << std::forward< A >(a);
return *this;
}
};
inline void operator<<=( const so_5::mbox_t & to, msg_maker & maker )
{
to <<= maker.m_os.str();
}
class a_barber_t : public so_5::agent_t
{
const so_5::mbox_t m_log;
unsigned m_haircuts = 0;
public :
a_barber_t( context_t ctx, so_5::mbox_t log )
: so_5::agent_t( ctx )
, m_log( std::move( log ) )
{}
virtual void so_define_agent() override
{
so_default_state()
.event( [&]( const customer_waiting & evt ) {
m_log <<= "barber is cutting hair";
so_5::send< start_haircut >( evt.customer );
std::this_thread::sleep_for(
std::chrono::milliseconds( 1000 * (1+(rand()%5)) ) );
so_5::send< done_haircut >( evt.customer );
++m_haircuts;
} )
.event< no_customers >( [&] {
m_log <<= "baber is sleeping";
} )
.event< closing_time >( [&] {
m_log <<= msg_maker() << "barber is going home. He did "
<< m_haircuts << " haircuts today";
} );
}
virtual void so_evt_start() override
{
m_log <<= "barber is sleeping";
}
};
class a_shop_t : public so_5::agent_t
{
const so_5::mbox_t m_log;
const so_5::mbox_t m_barber;
unsigned m_waiting_customers = 0;
const unsigned m_max_waiting_customers = 3;
const state_t st_open{ this, "open" };
const state_t st_closing{ this, "closing" };
const state_t st_closed{ this, "closed" };
public :
a_shop_t( context_t ctx, so_5::mbox_t log, so_5::mbox_t barber )
: so_5::agent_t( ctx )
, m_log( std::move( log ) )
, m_barber( std::move( barber ) )
{}
virtual void so_define_agent() override
{
this >>= st_open;
st_open
.event( [&]( const customer_enters & evt ) {
++m_waiting_customers;
m_log <<= msg_maker() << "shop has " << m_waiting_customers << " customers";
if( m_waiting_customers <= m_max_waiting_customers )
so_5::send< customer_waiting >( m_barber, evt.customer );
else
so_5::send< no_room >( evt.customer );
} )
.event< customer_leaves >( [&] {
if( !--m_waiting_customers )
{
m_log <<= "last customer left shop";
so_5::send< no_customers >( m_barber );
}
else
m_log <<= msg_maker() << "shop has " << m_waiting_customers << " customers";
} )
.event< closing_time >( [&] {
this >>= st_closing;
m_log <<= "shop closing";
so_5::send< closing_time >( m_barber );
} );
st_closing
.event( [&]( const customer_enters & evt ) {
++m_waiting_customers;
m_log <<= "customer turned away because shop closed";
so_5::send< shop_closed >( evt.customer );
} )
.event< customer_leaves >( [&] {
if( !--m_waiting_customers )
{
m_log <<= "last customer left shop";
this >>= st_closed;
m_log <<= "shop closed";
so_deregister_agent_coop_normally();
}
} );
}
virtual void so_evt_start() override
{
m_log <<= "shop opens";
}
};
class a_customer_t : public so_5::agent_t
{
const unsigned m_id;
const so_5::mbox_t m_log;
const so_5::mbox_t m_shop;
struct start_shopping : public so_5::signal_t {};
struct finish_shopping : public so_5::signal_t {};
void leave_baber_shop()
{
m_log <<= msg_maker() << "customer " << m_id << " leaves baber shop";
so_5::send< customer_leaves >( m_shop );
}
void go_home()
{
m_log <<= msg_maker() << "customer " << m_id << " is going home";
}
public :
a_customer_t( context_t ctx, unsigned id,
so_5::mbox_t log, so_5::mbox_t shop )
: so_5::agent_t( ctx )
, m_id( id )
, m_log( std::move( log ) )
, m_shop( std::move( shop ) )
{}
virtual void so_define_agent() override
{
so_default_state()
.event< start_shopping >( [&] {
m_log <<= msg_maker() << "customer " << m_id << " is shopping";
so_5::send_delayed< finish_shopping >( *this,
std::chrono::milliseconds( 500 * (rand()%20) ) );
} )
.event< finish_shopping >( [&] {
m_log <<= msg_maker() << "customer " << m_id << " goes into barber shop";
so_5::send< customer_enters >( m_shop, so_direct_mbox() );
} )
.event< start_haircut >( [&] {
m_log <<= msg_maker() << "customer " << m_id << " is having a haircut";
} )
.event< done_haircut >( [&] {
m_log <<= msg_maker() << "customer " << m_id << " is done having a haircut";
leave_baber_shop();
go_home();
} )
.event< no_room >( [&] {
m_log <<= msg_maker() << "customer " << m_id << " leaves because there is no room";
leave_baber_shop();
so_5::send< start_shopping >( *this );
} )
.event< shop_closed >( [&] {
m_log <<= msg_maker() << "customer " << m_id << " finds barber shop is closed";
leave_baber_shop();
go_home();
} );
}
virtual void so_evt_start() override
{
so_5::send< start_shopping >( *this );
}
};
int main()
{
try
{
so_5::launch( []( so_5::environment_t & env ) {
env.introduce_coop( [&]( so_5::coop_t & coop ) {
auto logger = make_logger( coop );
auto barber = coop.make_agent_with_binder< a_barber_t >(
so_5::disp::one_thread::create_private_disp( env )->binder(),
logger )->so_direct_mbox();
auto barbershop = coop.make_agent< a_shop_t >(
logger, barber )->so_direct_mbox();
unsigned const count = 20;
for( unsigned i = 0; i != count; ++i )
coop.make_agent< a_customer_t >( i, logger, barbershop );
so_5::send_delayed< closing_time >( env, barbershop,
std::chrono::seconds( 20 ) );
} );
} );
return 0;
}
catch( const std::exception & x )
{
std::cerr << "Exception: " << x.what() << std::endl;
}
return 2;
}
|
|
Комментариев нет:
Отправить комментарий