Sharing Memory

[Previous] [Next]

In the initial design phase of a distributed application, you must decide how to share data across a set of clients. Using shared memory in a COM+ application is typically the fastest way to accomplish this goal. It's faster than accessing a database because the data is close at hand. Moreover, you don't have to go through a database engine to read or write a data item. However, the use of shared memory in a COM+ application requires attention to synchronization. Different objects running on different threads will all access the same information.

Apartments and Standard Module Variables

Many veteran Visual Basic programmers get confused the first time they run their code in a multithreaded environment such as a COM+ application because public variables defined in standard (.BAS) modules aren't really global variables. These public variables are scoped at the STA level rather than at the process level. This changes things for programmers who are used to defining global, applicationwide variables in earlier versions of Visual Basic. The rules change when you start programming for the middle tier.

When your component runs in a multithreaded process, two objects see the same public standard-module data only when they're in the same STA. Objects in separate STAs see different instances of these public variables, as shown in Figure 7-7. This creates random behavior in a COM+ application because of the arbitrary way in which objects are matched with STA threads.

click to view at full size.

Figure 7-7 Any variable that you define in a standard module in an ActiveX DLL project is held in TLS. This means that objects running in different STAs can't share data.

When the Visual Basic team added support for the STA threading model, they had to decide how to handle the public variables defined in standard modules. If they decided to store this data in shared memory, someone would have to be responsible for synchronizing access to it. As in the case of the COM support they built into Visual Basic components, the team felt that it was unacceptable to require any type of programmer-assisted synchronization. Therefore, they opted to store the data defined in a standard module in TLS.

When the Visual Basic runtime encounters a new STA, it creates a fresh copy in TLS of each variable defined in a standard module. The Visual Basic runtime also calls Sub Main once in each STA. This lets you initialize the data on a per-STA basis. The important point to note is that there are several copies of each piece of data. While it can be acceptable to use constants or variables that are read-only once they're initialized, you can't use standard modules to define shared data that will be updated by clients on an ongoing basis. In a COM+ application, you must share read-write memory using another technique.

The Shared Property Manager

The COM+ Services Type Library includes a component known as the Shared Property Manager (SPM). (Its nickname is, of course, "the spam.") The SPM allows you to create and access shared data items in a COM+ application. Each data item is accessible to all objects running in the same process. The SPM also implements a locking scheme to synchronize access to data. This locking scheme can be critical to maintaining data consistency in the presence of concurrency.

The SPM provides a simple yet effective form of synchronization. Instead of having stand-alone properties, the SPM requires you to define each property within the scope of a property group. A property group provides a namespace for a set of properties. A property group is also a unit of synchronization. Going back to the example earlier in the chapter, what if you have data relating to a point and you want a writer object to change both the x coordinate and the y coordinate before a reader object can see either change? A property group provides a locking scheme that allows the writer object to finish its work before any reader object can see its changes.

Figure 7-8 shows how the SPM works from a high-level perspective. Different objects running in different activities each creates its own instance of a specific property group. However, the data for the group is laid out in memory only once in a processwide fashion. In addition to the property values, each property group has its own exclusive lock. Each shared property group instance implements an internal lock manager that relies on this exclusive lock to synchronize access to property data within the group.

The SPM is a name/value dictionary that lets you create and retrieve properties and property groups by name. Let's look at an example. Before you can create and retrieve properties and property groups, you must create an instance of the shared property group manager. Take a look at the following code:

 Dim spam As SharedPropertyGroupManager Set spam = New SharedPropertyGroupManager Dim MyPoint As SharedPropertyGroup Dim AlreadyExists As Boolean ' Create and/or bind to group MyPointData Set MyPoint = spam.CreatePropertyGroup("MyPointData", _                                         LockMethod, _                                         Process, _                                         AlreadyExists) 

click to view at full size.

Figure 7-8 The SPM provides synchronized access to shared data through properties in a property group. When you create a property group, you define its locking behavior by setting the isolation mode.

The CreatePropertyGroup method takes four parameters. The first parameter lets you pass the name of the property group. If a property group of the same name already exists, the SPM binds you to it. If the property group doesn't exist, the SPM creates it and then binds you to it. The last parameter of CreatePropertyGroup is a Boolean output parameter that tells the caller whether the property group existed before the call.

The second and third parameters specify an isolation mode and a release mode for your property group. The isolation mode allows you to establish the locking behavior for the group, while the release mode tells the SPM how long you want the data for the group to remain in memory. Note that both of these parameters are passed ByRef and that the SPM ignores their input values if the property group already exists. Only the client that creates the group can set these values. If the property group already exists, the SPM returns the current isolation mode and release mode to the caller in these two parameters as output values.

When your object creates a property group instance and attempts to access some of its properties, it must first acquire the group's exclusive lock. Internally, the lock is associated with the activity in which your object is running. Once your object's activity has acquired the lock, objects in other activities will block when they attempt to access properties in the group. The next question is: How long does the activity hold to the lock? The answer depends on how you set up the group's locking behavior.

When you create a group, you establish the locking behavior by using an isolation mode of LockMethod or LockSetGet. You should use LockMethod if you need to maintain consistency across several of the properties in the group. When a group's isolation mode is set to LockMethod, each object acquires the exclusive lock and holds it for the duration of the current method call. The COM+ runtime supplies code in the interception layer to release the lock after your method returns. This scheme allows an object to read and write to several properties in a group without having to worry about the reads and writes of other objects.

You have a few good reasons to create a property group using an isolation level of LockMethod. You might want to read a property and then update its value without the risk of another client reading or updating its value between your two operations. Or you might have interdependencies among the properties in a group, as in the case of the data associated with a point. For example, you might want to change both the x position and the y position of a point without the risk of another client getting an inconsistent read. In such cases, each object requires a higher level of isolation when running a series of updates.

If you pass LockSetGet, the SPM acquires and releases the group's exclusive lock each time you access an individual property. Unlike with LockMethod, a lock is never held for the duration of a method. You should note that in some designs this can make your data vulnerable to inconsistency. However, when you don't need higher levels of isolation, holding locks for a shorter duration results in higher levels of concurrency.

You have to find the best balance between consistency and concurrency. Often it's desirable to place different properties in different groups. Some of your groups can use LockMethod while others use LockSetGet. This lets you add more granularity to your locking scheme to get the consistency you need without blocking users unnecessarily.

When you set the release mode for a group, you can pass a value of either Process or Standard. The value Process indicates that you want the property group to remain in memory for the lifetime of the process. The value Standard indicates that you want the property group to manage its lifetime using standard reference counting. When you pass Standard, the property group's data is released from memory as soon as the last client releases its property group object. In most cases, you'll create and release property group objects, but you'll want to keep the data associated with the group in memory. This means that you'll use Process much more often than Standard.

Of course, the SPM isn't really useful until you start creating and using shared properties. You can create a property using the CreateProperty method or the CreatePropertyByPosition method. Here's an example of creating two properties and setting their default values:

 Dim spam As SharedPropertyGroupManager Set spam = New SharedPropertyGroupManager Dim MyPoint As SharedPropertyGroup Dim AlreadyExists As Boolean Set MyPoint = spam.CreatePropertyGroup("MyPointData", _                                         LockMethod, _                                         Process, _                                         AlreadyExists) ' Create and/or bind to property X Dim X_Position As SharedProperty Set X_Position = MyPoint.CreateProperty("X", AlreadyExists) If AlreadyExists = False Then X_Position.Value = 10 ' Create and/or bind to property Y Dim Y_Position As SharedProperty Set Y_Position = MyPoint.CreateProperty("Y", AlreadyExists) If AlreadyExists = False Then Y_Position.Value = 10 

Like CreatePropertyGroup, CreateProperty simply binds you to a property if it already exists. You can retrieve an existing property by name or by position. If the property doesn't already exist, the SPM creates it and assigns the default value 0 (as a long integer) to its Value property. If you want a different default value, you should set it explicitly when the property is created.

The Value property of a shared property object is stored as a Variant. This situation gives you quite a bit of flexibility. You can store any Variant-compliant data type such as Integer, Long, Double, or String in a property. You can also store a Variant-compliant array. The following is an example of storing an array of strings in a shared property:

 Dim data() As String ReDim data(3) data(0) = "Bob" data(1) = "Carol" data(2) = "Ted" data(3) = "Alice" MyProperty.Value = data 

Once you're familiar with the SPM, you'll find that it's pretty easy to share data across a set of objects running in different activities. In a moment of weakness, you might even be tempted to assign a Visual Basic object reference to a shared property. However, you must resist this impulse. As you know, shared objects don't fit very well into the activity-based programming model. Because of their problems with thread affinity, shared Visual Basic objects don't fit in at all. There are also problems associated with context relativity. If you store an object reference in a property group, you can legally use the reference only from within the same context. The bottom line is that the SPM isn't for storing objects created with Visual Basic.

While storing object references in a property group is unacceptable, it's common to create a component for managing the data held in a specific group. It's just that your application will create objects from this component and release them even though the property group data remains in memory. The data in the property group defines a logical object in terms of primitive data types and arrays. You can simply create objects when they're needed and let them rehydrate themselves with the data in the property group. As long as each object lives entirely within the scope of a single activity, you can avoid problems relating to concurrency.

Limitations of the SPM

The SPM provides data that you can access quickly, but you should note that the data will be lost in the case of a system failure or an application crash. The reason that the SPM is fast is because nothing is written to disk. If the data you're sharing across a set of clients needs to be recoverable, you must use some persistent form of storage such as a database.

Data managed by the SPM is accessible only to objects running in a single process. If you need to share data across processes or machines, you must use something else. Once again, this might be a good reason to employ a database.

Keep in mind that read and write operations in the SPM are never transacted. While the SPM can provide isolation, it doesn't provide any rollback facilities. If you modify a shared property from inside a COM+ transaction, your changes are permanent, even when you roll back the transaction. And don't be fooled into thinking that your property group locks are held to the end of the current COM+ transaction. The locks in the SPM have nothing to do with COM+ transactions. The SPM releases its locks as soon as the current method finishes, which in most cases is before the end of the current transaction.

Also note that, unlike most database products, the SPM never uses read locks. It employs only exclusive locking. Once an object acquires the lock for a group, every other object is blocked. A database can provide an optimized form of concurrency by blocking only users who attempt to perform write operations. This additional level of locking granularity provides higher levels of concurrency because many readers can access the same data at the same time.

During the beta cycle for Windows 2000, COM+ included a service known as the In-Memory Database (IMDB). The IMDB, which was promoted as an improved version of the SPM, allowed operations on shared memory to be enlisted in transactions. It also provided more granularity with respect to locking. However, the IMDB had various problems, and customer feedback during the beta cycle ultimately led the COM+ team to pull the IMDB from the final release of Windows 2000.

While the IMDB was accessible through an OLE-DB provider and ADO, it didn't provide a database engine for running queries. Therefore, it wasn't very functional from the perspective of an experienced database developer. The real downfall of the IMDB was that it couldn't outperform a local Microsoft SQL Server database. In short, it was harder to use and not as functional, and it was no faster than maintaining shared data in a table in SQL Server.

I've taken the time to discuss the IMDB not so you'll know about things you can't use but to illustrate the benefits of using a local database to get around the limitations of the SPM. The SPM will always provide the fastest access to shared data, but if you need persistence, data that's shared across processes, read locks, or transacted operations, a local database can provide that extra support. As a COM+ developer, you should always think of the database as your friend. Don't be afraid to use it when you need it.



Programming Distributed Applications with COM+ and Microsoft Visual Basic 6.0
Programming Distributed Applications with Com and Microsoft Visual Basic 6.0 (Programming/Visual Basic)
ISBN: 1572319615
EAN: 2147483647
Year: 2000
Pages: 70
Authors: Ted Pattison

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net