<!-- page07.html,v 1.9 2000/03/19 20:09:27 jcej Exp --> <HTML> <HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> <META NAME="Author" CONTENT="James CE Johnson"> <TITLE>ACE Tutorial 013</TITLE> </HEAD> <BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#000FFF" VLINK="#FF0F0F"> <CENTER><B><FONT SIZE=+2>ACE Tutorial 013</FONT></B></CENTER> <CENTER><B><FONT SIZE=+2>Multiple thread pools</FONT></B></CENTER> <P> <HR WIDTH="100%"> <P> I've been trying to justify the chain of tasks by talking about a Work object that implements a state machine. The idea is that your Work object has to perform a series of discrete steps to complete it's function. Traditionally, all of those steps would take place in one thread of execution. That thread would probably be one from a Task thread pool. <P> Suppose, however, that some of those steps spend a lot of time waiting for disk IO. You could find that all of your thread-pool threads are just sitting there waiting for the disk. You might then be tempted to increase the thread pool size to get more work through. However, if some of the stages are memory intensive, you could run out of memory if all of the workers get to that state at the same time. <P> One solution might be to have different thread pools for each state. Each pool could have it's size tuned appropriately for the work that would be done there. That's where the chain of Tasks comes in. In this tutorial's implementation I've taken the easy route and set all of the thread pools to the same size but a more realistic solution would be to set each thread pool in the chain to a specific size as needed by that state of operation. <P> There's not much to this header either so I've combined it with the cpp file as with task. <P> <HR WIDTH="100%"> <HR width=50%><P><center>work.h</center><HR width=50%> <PRE> <font color=red>// page07.html,v 1.9 2000/03/19 20:09:27 jcej Exp</font> <font color=blue>#ifndef</font> <font color=purple>WORK_H</font> <font color=blue>#define</font> <font color=purple>WORK_H</font> <font color=blue>#include</font> "<A HREF="../../../ace/Log_Msg.h">ace/Log_Msg.h</A>" <font color=blue>#if !defined</font> (<font color=purple>ACE_LACKS_PRAGMA_ONCE</font>) <font color=blue># pragma</font> <font color=purple>once</font> <font color=blue>#endif</font> <font color=red>/* ACE_LACKS_PRAGMA_ONCE */</font> <font color=blue>#include</font> "<A HREF="../../../ace/Synch.h">ace/Synch.h</A>" <font color=blue>#include</font> "<font color=green>mld.h</font>" <font color=red>/* Our specilized message queue and thread pool will know how to do "<font color=green>work</font>" on our Unit_Of_Work baseclass. */</font> class Unit_Of_Work { public: Unit_Of_Work (void); virtual ~ Unit_Of_Work (void); <font color=red>// Display the object instance value</font> void who_am_i (void); <font color=red>// The baseclass can override this to show it's "<font color=green>type name</font>"</font> virtual void what_am_i (void); <font color=red>// This is where you do application level logic. It will be</font> <font color=red>// called once for each thread pool it passes through. It</font> <font color=red>// would typically implement a state machine and execute a</font> <font color=red>// different state on each call.</font> virtual int process (void); <font color=red>// This is called by the last Task in the series (see task.h)</font> <font color=red>// in case our process() didn't get through all of it's states.</font> virtual int fini (void); protected: ACE_Atomic_Op < ACE_Mutex, int >state_; MLD; }; <font color=red>/* A fairly trivial work derivative that implements an equally trivial state machine in process() */</font> class Work : public Unit_Of_Work { public: Work (void); Work (int message); virtual ~ Work (void); void what_am_i (void); int process (void); int fini (void); protected: int message_; MLD; }; <font color=blue>#endif</font> </PRE> <HR width=50%><P><center>work.cpp</center><HR width=50%> <PRE> <font color=red>// page07.html,v 1.9 2000/03/19 20:09:27 jcej Exp</font> <font color=blue>#include</font> "<font color=green>work.h</font>" <font color=red>/* Initialize the state to zero */</font> <font color=#008888>Unit_Of_Work::Unit_Of_Work</font> (void) : state_ (0) { ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Unit_Of_Work ctor\n</font>", (void *) this)); } <font color=#008888>Unit_Of_Work::~Unit_Of_Work</font> (void) { ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Unit_Of_Work dtor\n</font>", (void *) this)); } <font color=red>/* Display our instance value */</font> void <font color=#008888>Unit_Of_Work::who_am_i</font> (void) { ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Unit_Of_Work instance\n</font>", (void *) this)); } <font color=red>/* Dispay our type name */</font> void <font color=#008888>Unit_Of_Work::what_am_i</font> (void) { ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x I am a Unit_Of_Work object\n</font>", (void *) this)); } <font color=red>/* Return failure. You should always derive from Unit_Of_Work... */</font> int <font color=#008888>Unit_Of_Work::process</font> (void) { return -1; } <font color=red>/* ditto */</font> int <font color=#008888>Unit_Of_Work::fini</font> (void) { return -1; } <font color=red>/* Default constructor has no "<font color=green>message number</font>" */</font> <font color=#008888>Work::Work</font> (void) :message_ (-1) { ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Work ctor\n</font>", (void *) this)); } <font color=red>/* The useful constructor remembers which message it is and will tell you if you ask. */</font> <font color=#008888>Work::Work</font> (int message) : message_ (message) { ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Work ctor for message %d\n</font>", (void *) this, message_)); } <font color=#008888>Work::~Work</font> (void) { ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Work dtor\n</font>", (void *) this)); } <font color=red>/* This objects type name is different from the baseclass */</font> void <font color=#008888>Work::what_am_i</font> (void) { ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x I am a Work object for message %d\n</font>", (void *) this, message_)); } <font color=red>/* A very simple state machine that just walks through three stages. If it is called more than that, it will tell you not to bother. */</font> int <font color=#008888>Work::process</font> (void) { switch (++state_) { case 1: ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Stage One\n</font>", (void *) this)); break; case 2: ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Stage Two\n</font>", (void *) this)); break; case 3: ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x Stage Three\n</font>", (void *) this)); break; default: ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x No work to do in state %d\n</font>", (void *) this, state_.value ())); break; } return (0); } <font color=red>/* If you don't have enough subtasks in the chain then the state machine won't progress to the end. The fini() hook will allow us to recover from that by executing the remaining states in the final task of the chain. */</font> int <font color=#008888>Work::fini</font> (void) { while (state_.value () < 3) { ACE_DEBUG ((LM_DEBUG, "<font color=green>(%P|%t) 0x%x <font color=#008888>Work::fini</font>() state %d\n</font>", (void *) this,state_.value())); if (this->process () == -1) { ACE_ERROR_RETURN ((LM_ERROR, "<font color=green>%p\n</font>", "<font color=green>process</font>"), -1); } } return (0); } </PRE> <HR> <P> And that is that. For a more complex machine that may want to "jump states" you would have to set some "state information" (sorry, bad choice of terminology again) so that process() could decide what to do at each call. You might also modify Task::svc() so that it will respect the return value of process() and do something useful with the information. <P> <P><HR WIDTH="100%"> <CENTER>[<A HREF="../online-tutorials.html">Tutorial Index</A>] [<A HREF="page08.html">Continue This Tutorial</A>]</CENTER>