Channels ▼
RSS

.NET

Prefer Using Active Objects Instead of Naked Threads


A C++0x Version

The aforementioned OO-style helper works well in any mainstream language, but in any given language we can often make it even easier by using local language features and idioms. For example, if we were writing it in C++, we could observe that Message and its derived classes are simply applying the usual "OO way" of rolling your own function objects (or functors), and Execute could as well be spelled operator(), the function call operator. The only reason for the Message base class is to provide a way to hold and later invoke an arbitrary message, whereas in C++0x we already have std::function<> as a handy way to hold and later invoke any suitable callable function or functor.

So let's leverage the convenience of C++ function objects. We'll avoid a lot of the "OO Message hierarchy" boilerplate. Active will be a simpler class. Derived classes will be easier to write. What's not to like?


// Example 2: Active helper, in idiomatic C++(0x)
//
class Active {
public:
  typedef function<void()> Message;

private:

  Active( const Active& );           // no copying
  void operator=( const Active& );    // no copying

  bool done;                         // le flag
  message_queue<Message> mq;        // le queue
  unique_ptr<thread> thd;          // le thread

  void Run() {
    while( !done ) {
      Message msg = mq.receive();
      msg();            // execute message
    } // note: last message sets done to true
  }

public:

  Active() : done(false) {
    thd = unique_ptr<thread>(
                  new thread( [=]{ this->Run(); } ) );
  }

  ~Active() {
    Send( [&]{ done = true; } ); ;
    thd->join();
  }

  void Send( Message m ) {
    mq.send( m );
  }
};

Next, we can use the lambda functions language feature to make an implementing class like Backgrounder even simpler to write, because we don't even have to write the external "launcher" method and the actual body in different places…we can simply write each method the same way we write it naturally, and send the body of the message as a lambda function:


class Backgrounder {
public:
  void Save( string filename ) { a.Send( [=] {
    …
  } ); }

  void Print( Data& data ) { a.Send( [=, &data] {
    …
  } ); }

private:
  PrivateData somePrivateStateAcrossCalls;
  Active a;
};

This isn't just a C++ trick, by the way. If you're using C#, which also has generalized delegates and lambda functions, you can do likewise. Here's a sketch of how the simplified Backgrounder code would look in C#:


class Backgrounder : IDisposable {
  public Backgrounder() { /*…*/ a = new Active(); }
  public Dispose() { a.Dispose(); }

  public void Save( String filename ) { a.Send( () => {
    …
  } ); }

  public void Print( Data data ) { a.Send( () => {
    …
  } ); }

private:
  PrivateData somePrivateStateAcrossCalls;
  Active a;
};

Summary

Unlike with free threading, which lets us randomly do anything at all, active objects make it easier to express what we mean by automatically organizing the private thread's work around an event-driven message pump, naturally expressing isolated private data as simple member data, and offering strong lifetime guarantees by letting us express thread and task lifetime in terms of object lifetime (and therefore directly leverage the rich support for object lifetime semantics already built into our programming languages). All of this raises the semantic level of our program code, and makes our program easier to write, read, and reason about. Major uses for active objects include the same motivating cases as for any threads: to express long-running services (for example, a physics thread or a GUI thread); to decouple independent work (for example, background save, or pipeline stages); to encapsulate resources (for example, I/O streams or certain shared objects). You may never need or want to write a naked thread again.

Coming Up

So far, we've looked only at two of the basics of active objects, namely their lifetimes and asynchronous method call semantics. Next month, we'll complete the overview by considering important remaining details — how to handle a methods' return values and/or out parameters, and other kinds of communication back to the caller. Stay tuned.

References

[1] H. Sutter. "Use Threads Correctly = Isolation + Asynchronous Messages" (Dr. Dobb's Digest, April 2009: http://www.drdobbs.com/high-performance-computing/215900465).

[2] R. Lavender and D. Schmidt. "Active Object: An Object Behavioral Pattern for Concurrent Programming" (update of paper published in Pattern Languages of Program Design 2, Addison-Wesley, 1996: http://www.cs.wustl.edu/~schmidt/PDF/Act-Obj.pdf).

[3] The ADAPTIVE Communication Environment (ACE): http://www.cs.wustl.edu/~schmidt/ACE.html.


Herb Sutter is a bestselling author and consultant on software development topics, and a software architect at Microsoft. He can be contacted at www.gotw.ca.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video