Rogue Wave banner
Previous fileTop of DocumentContentsIndex pageNext file
Threads Module User's Guide

3.8 Using Threads To Build Active Objects

You can encapsulate asynchronous operations within an active object. An active object creates one or more threads to asynchronously execute service requests made by its clients.

This is useful because IOUs (described in Section 5.5) are not always closed and redeemed when threads exit. Although the RWTThreadIOUFunction class closes the IOU just before exiting, it is possible to create threads that can close an IOU at any time during their processing.

3.8.1 Using Runnables

Using an active object solves the join problem by creating any necessary threads within the active object's constructor and joining with them in its destructor. This pattern is similar to the "resource acquisition is initialization" idiom employed by the Synchronization package guard objects (see Section 4.4.3).

Any of the threaded runnable classes, which are part of the Threading package, can construct an active object. The final choice, though, is dependent on the type of service that the object provides.

Runnables are covered in detail in Section 3.5, "The Runnable Object Classes."

3.8.1.1 Example

Example 22 shows how an asynchronous function call can be converted to a simple active object.

Example 22: Encapsulating asynchronous operations in an active object

//1

Defines the actual service that is to be executed in a separate thread.

//2

Defines a constructor that creates a thread to perform the operation.

//3

Defines a destructor that joins the thread.

//4

Includes an accessor for retrieving the IOU result of the operation.

//5

Constructs a named object to start the operation.

//6

Retrieves the future result of the operation as an IOU.

//7

Redeems the result, blocking (if necessary) until the operation has completed.

//8

Destroys the service object at end of scope, and automatically joins with the thread that was created.

3.8.1.2 Other Solutions to the Join Problem

The approach in the previous example solves the join problem, but the implementation is confusing because:

A better way to solve the problem is to move the initiation of the asynchronous operation to a public member function of the class. In this case, the operation is started by calling the member function, which creates a threaded runnable to perform the actual operation. A handle to that runnable could be stored within the class so that a join can be performed when the object is destroyed.

Other problems are created, however, if the service is invoked a second time:

To avoid these new problems:

3.8.2 Using Runnable Servers

The RWTThreadIOUFunction used in the example is generally not suitable for an active object implementation that must accept any number of asynchronous requests from a client interface.

It is better to use one of the runnable server classes, RWRunnableServer or RWServerPool, to provide the internal thread or threads required by the active object. These classes are suitable because they are designed to continuously accept and execute other runnable objects. This allows you to package individual operations as synchronous runnables that can be passed to the internal server for execution. Runnable servers are covered in detail in Section 3.6, "The Server Classes."

In Example 23, individual operations are executed asynchronously relative to the thread that requests them, but simultaneous requests cannot be processed concurrently. This limitation is a consequence of choosing the single-threaded RWRunnableServer class for the internal server thread.

If an active object design can benefit from increased concurrency, then the multithreaded RWServerPool class should be used instead.

Example 23: Using a single-threaded runnable server class

//1

Defines the actual service to be performed.

//2

Defines a constructor that creates and starts the internal server.

//3

Defines a destructor that stops the internal server and waits for it to exit.

//4

Defines a member function to initiate an asynchronous operation by constructing a synchronous runnable and passing it to the internal server. The IOU representing the result of the operation is returned to the caller.

//5

Constructs an instance of the service provider.

//6

Calls the asynchronous service and save the IOU result.

//7

Redeems the IOU result, blocking if necessary, until the result is made available.

//8

The service provider object is destroyed at block scope, but the destructor does not return until its internal server thread has shutdown and exited.



Previous fileTop of DocumentContentsNo linkNext file

Copyright © Rogue Wave Software, Inc. All Rights Reserved.

The Rogue Wave name and logo, and SourcePro, are registered trademarks of Rogue Wave Software. All other trademarks are the property of their respective owners.
Provide feedback to Rogue Wave about its documentation.