Home arrow C++ arrow Page 10 - Multithreading in C++
C++

Multithreading in C++


Multithreading is growing in importance in modern programming for a variety of reasons, not the least of which being that Windows supports multithreading. While C++ does not feature built-in support for multithreading, it can be used to created multithreaded programs, which is the subject of this article. It is taken from chapter three of The Art of C++, written by Herbert Schildt (McGraw-Hill/Osborne, 2004; ISBN: 0072255129).

Author Info:
By: McGraw-Hill/Osborne
Rating: 5 stars5 stars5 stars5 stars5 stars / 229
July 14, 2005
TABLE OF CONTENTS:
  1. · Multithreading in C++
  2. · An Overview of the Windows Thread Functions
  3. · Priority Classes
  4. · The Windows Synchronization Objects
  5. · The Thread Control Panel
  6. · A Closer Look at the Thread Control Panel
  7. · Demonstrating the Control Panel
  8. · A Multithreaded Garbage Collector
  9. · Synchronizing Access to gclist
  10. · The Entire Multithreaded Garbage Collector
  11. · Using the Multithreaded Garbage Collector

print this article
SEARCH DEVARTICLES

Multithreading in C++ - The Entire Multithreaded Garbage Collector
(Page 10 of 11 )

The entire multithreaded version of the garbage collector is shown here. Call this file gcthrd.h.

// A garbage collector that runs as a back ground task.
#include <iostream>
#include <list>
#include <typeinfo>
#include <cstdlib>
#include <windows.h>
#include <process.h>
using namespace std;
// To watch the action of the garbage collector, define DISPLAY.
// #define DISPLAY
// Exception thrown when an attempt is made to
// use an Iter that exceeds the range of the
// underlying object.
//
class OutOfRangeExc {
  // Add functionality if needed by your application.
};
// Exception thrown when a time-out occurs
// when waiting for access to hMutex.
//
class TimeOutExc {
  // Add functionality if needed by your application.
};
// An iterator-like class for cycling through arrays
// that are pointed to by GCPtrs. Iter pointers
// ** do not ** participate in or affect garbage
// collection. Thus, an Iter pointing to
// some object does not prevent that object
// from being recycled.
//
template <class T> class Iter {
  T *ptr;   // current pointer value
  T *end;   // points to element one past end
  T *begin; // points to start of allocated array
  unsigned length; // length of sequence
public:
  Iter() {
    ptr = end = begin = NULL;
    length = 0;
  }
  Iter(T *p, T *first, T *last) {
    ptr = p;
    end = last;
    begin = first;
    length = last - first;
  }
  // Return length of sequence to which this
  // Iter points.
  unsigned size() { return length; }
  // Return value pointed to by ptr.
  // Do not allow out-of-bounds access.
  T &operator*() {
    if( (ptr >= end) || (ptr < begin) )
      throw OutOfRangeExc();
    return *ptr;
  }
  // Return address contained in ptr.
  // Do not allow out-of-bounds access.
  T *operator->() {
    if( (ptr >= end) || (ptr < begin) )
      throw OutOfRangeExc();
    return ptr;
  }
  // Prefix ++.
  Iter operator++() {
    ptr++;
    return *this;
  }
  // Prefix --.
  Iter operator--() {
    ptr--;
    return *this;
  }
  // Postfix ++.
  Iter operator++(int notused) {
    T *tmp = ptr;
    ptr++;
    return Iter<T>(tmp, begin, end);
  }
  // Postfix --.
  Iter operator--(int notused) {
    T *tmp = ptr;
    ptr--;
    return Iter<T>(tmp, begin, end);
  }
  // Return a reference to the object at the
  // specified index. Do not allow out-of-bounds
  // access.
  T &operator[](int i) {
    if( (i < 0) || (i >= (end-begin)) )
      throw OutOfRangeExc();
    return ptr[i];
  }
  // Define the relational operators.
  bool operator==(Iter op2) {
    return ptr == op2.ptr;
  }
  bool operator!=(Iter op2) {
    return ptr != op2.ptr;
  }
  bool operator<(Iter op2) {
    return ptr < op2.ptr;
  }
  bool operator<=(Iter op2) {
    return ptr <= op2.ptr;
  }
  bool operator>(Iter op2) {
    return ptr > op2.ptr;
  }
  bool operator>=(Iter op2) {
    return ptr >= op2.ptr;
  }
  // Subtract an integer from an Iter.
  Iter operator-(int n) {
    ptr -= n;
    return *this;
  }
  // Add an integer to an Iter.
  Iter operator+(int n) {
    ptr += n;
    return *this;
  }
  // Return number of elements between two Iters.
  int operator-(Iter<T> &itr2) {
    return ptr - itr2.ptr;
  }
};
// This class defines an element that is stored
// in the garbage collection information list.
//
template <class T> class GCInfo {
public:
  unsigned refcount; // current reference count
  T *memPtr; // pointer to allocated memory
  /* isArray is true if memPtr points
     to an allocated array. It is false
     otherwise. */
  bool isArray; // true if pointing to array
  /* If memPtr is pointing to an allocated
     array, then arraySize contains its size */
  unsigned arraySize; // size of array
  // Here, mPtr points to the allocated memory.
  // If this is an array, then size specifies
  // the size of the array.
  GCInfo(T *mPtr, unsigned size=0) {
    refcount = 1;
    memPtr = mPtr;
    if(size != 0)
      isArray = true;
    else
      isArray = false;
    arraySize = size;
  }
};
// Overloading operator== allows GCInfos to be compared.
// This is needed by the STL list class.
template <class T> bool operator==(const GCInfo<T> &ob1,
                const GCInfo<T> &ob2) {
  return (ob1.memPtr == ob2.memPtr);
}
// GCPtr implements a pointer type that uses
// garbage collection to release unused memory.
// A GCPtr must only be used to point to memory
// that was dynamically allocated using new.
// When used to refer to an allocated array,
// specify the array size.
//
template <class T, int size=0> class GCPtr {
  // gclist maintains the garbage collection list.
  static list<GCInfo<T> > gclist;
  // addr points to the allocated memory to which
  // this GCPtr pointer currently points.
  T *addr;
  /* isArray is true if this GCPtr points
     to an allocated array. It is false
     otherwise. */
  bool isArray; // true if pointing to array
  // If this GCPtr is pointing to an allocated
  // array, then arraySize contains its size.
  unsigned arraySize; // size of the array
  // These support multithreading.
  unsigned tid; // thread id
  static HANDLE hThrd; // thread handle
  static HANDLE hMutex; // handle of mutex
  static int instCount; // counter of GCPtr objects
  // Return an iterator to pointer info in gclist.
  typename list<GCInfo<T> >::iterator findPtrInfo(T *ptr);
public:
  // Define an iterator type for GCPtr<T>.
  typedef Iter<T> GCiterator;
  // Construct both initialized and uninitialized objects.
  GCPtr(T *t=NULL) {
    // When first object is created, create the mutex
    // and register shutdown().
    if(hMutex==0) {
      hMutex = CreateMutex(NULL, 0, NULL);
      atexit(shutdown);
    }
    if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT)
      throw TimeOutExc();
    list<GCInfo<T> >::iterator p;
    p = findPtrInfo(t);
    // If t is already in gclist, then
    // increment its reference count.
    // Otherwise, add it to the list.
    if(p != gclist.end())
      p->refcount++; // increment ref count
    else {
      // Create and store this entry.
      GCInfo<T> gcObj(t, size);
      gclist.push_front(gcObj);
    }
    addr = t;
    arraySize = size;
    if(size > 0) isArray = true;
    else isArray = false;
    // Increment instance counter for each new object.
    instCount++;
    // If the garbage collection thread is not
    // currently running, start it running.
    if(hThrd==0) {
      hThrd = (HANDLE) _beginthreadex(NULL, 0, gc,
              (void *) 0, 0, (unsigned *) &tid);
    // For some applications, it will be better
    // to lower the priority of the garbage collector
    // as shown here:
    //
    // SetThreadPriority(hThrd,
    //                   THREAD_PRIORITY_BELOW_NORMAL);
  }
  ReleaseMutex(hMutex);
}
// Copy constructor.
GCPtr(const GCPtr &ob) {
  if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT)
    throw TimeOutExc();
  list<GCInfo<T> >::iterator p;
  p = findPtrInfo(ob.addr);
  p->refcount++; // increment ref count
  addr = ob.addr;
  arraySize = ob.arraySize;
  if(arraySize > 0) isArray = true;
  else isArray = false;
  instCount++; // increase instance count for copy
  ReleaseMutex(hMutex);
}
// Destructor for GCPTr.
~GCPtr();
// Collect garbage. Returns true if at least
// one object was freed.
static bool collect();
// Overload assignment of pointer to GCPtr.
T *operator=(T *t);
// Overload assignment of GCPtr to GCPtr.
GCPtr &operator=(GCPtr &rv);
// Return a reference to the object pointed
// to by this GCPtr.
T &operator*() {
  return *addr;
}
// Return the address being pointed to.
T *operator->() { return addr; }
// Return a reference to the object at the
// index specified by i.
T &operator[](int i) {
  return addr[i];
}
// Conversion function to T *.
operator T *() { return addr; }
// Return an Iter to the start of the allocated memory. Iter<T> begin() {
  int size;
  if(isArray) size = arraySize;
  else size = 1;
  return Iter<T>(addr, addr, addr + size);
}
// Return an Iter to one past the end of an allocated array.
Iter<T> end() {
  int size;
  if(isArray) size = arraySize;
  else size = 1;
  return Iter<T>(addr + size, addr, addr + size);
}
// Return the size of gclist for this type
// of GCPtr.
  static int gclistSize() {
    if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT) 
      throw TimeOutExc();
    unsigned sz = gclist.size();
    ReleaseMutex(hMutex);
    return sz;
  }
  // A utility function that displays gclist.
  static void showlist();
  // The following functions support multithreading.
  //
  // Returns true if the collector is still in use.
  static bool isRunning() { return instCount > 0; }
  // Clear gclist when program exits.
  static void shutdown();
  // Entry point for garbage collector thread.
  static unsigned __stdcall gc(void * param);
};
// Create storage for the static variables.
template <class T, int size>
  list<GCInfo<T> > GCPtr<T, size>::gclist;
template <class T, int size>
  int GCPtr<T, size>::instCount = 0;
template <class T, int size>
  HANDLE GCPtr<T, size>::hMutex = 0;
template <class T, int size>
  HANDLE GCPtr<T, size>::hThrd = 0;
// Destructor for GCPtr.
template <class T, int size>
GCPtr<T, size>::~GCPtr() {
  if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT)
    throw TimeOutExc();
  list<GCInfo<T> >::iterator p;
  p = findPtrInfo(addr);
  if(p->refcount) p->refcount--; // decrement ref count
  // Decrement instance counter for each object
  // that is destroyed.
  instCount--;
  ReleaseMutex(hMutex);
}
// Collect garbage. Returns true if at least
// one object was freed.
template <class T, int size>
bool GCPtr<T, size>::collect() {
  if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT)
    throw TimeOutExc();
  bool memfreed = false;
  list<GCInfo<T> >::iterator p;
  do {
    // Scan gclist looking for unreferenced pointers.
    for(p = gclist.begin(); p != gclist.end(); p++) {
      // If in-use, skip.
      if(p->refcount > 0) continue;
      memfreed = true;
      // Remove unused entry from gclist.
      gclist.remove(*p);
      // Free memory unless the GCPtr is null.
      if(p->memPtr) {
        if(p->isArray) {
          delete[] p->memPtr; // delete array
        }
        else {
          delete p->memPtr; // delete single element
        }
      }
      // Restart the search.
      break;
    }
  } while(p != gclist.end());
  ReleaseMutex(hMutex);
  return memfreed;
}
// Overload assignment of pointer to GCPtr.
template <class T, int size>
T * GCPtr<T, size>::operator=(T *t) {
  if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT)
    throw TimeOutExc();
  list<GCInfo<T> >::iterator p;
  // First, decrement the reference count
  // for the memory currently being pointed to.
  p = findPtrInfo(addr);
  p->refcount--;
  // Next, if the new address is already
  // existent in the system, increment its
  // count. Otherwise, create a new entry
  // for gclist.
  p = findPtrInfo(t);
  if(p != gclist.end())
    p->refcount++;
  else {
    // Create and store this entry.
    GCInfo<T> gcObj(t, size);
    gclist.push_front(gcObj);
  }
  addr = t; // store the address.
  ReleaseMutex(hMutex);
  return t;
}
// Overload assignment of GCPtr to GCPtr.
template <class T, int size>
GCPtr<T, size> & GCPtr<T, size>::operator=(GCPtr &rv) {
  if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT)
    throw TimeOutExc();
  list<GCInfo<T> >::iterator p;
  // First, decrement the reference count
  // for the memory currently being pointed to.
  p = findPtrInfo(addr);
  p->refcount--;
  // Next, increment the reference count of
  // of the new object.
  p = findPtrInfo(rv.addr);
  p->refcount++; // increment ref count
  addr = rv.addr;// store the address.
  ReleaseMutex(hMutex);
  return rv;
}
// A utility function that displays gclist.
template <class T, int size>
void GCPtr<T, size>::showlist() {
  if(WaitForSingleObject(hMutex, 10000)==WAIT_TIMEOUT)
    throw TimeOutExc();
  list<GCInfo<T> >::iterator p;
  cout << "gclist<" << typeid(T).name() << ", "
       << size << ">:\n";
  cout << "memPtr     refcount    value\n";
  if(gclist.begin() == gclist.end()) {
    cout << "           -- Empty --\n\n";
    return;
  }
  for(p = gclist.begin(); p != gclist.end(); p++) {
    cout <<  "[" << (void *)p->memPtr << "]"
         << "     " << p->refcount << "      ";
    if(p->memPtr) cout << "  " << *p->memPtr;
    else cout << "   ---";
    cout << endl;
  }
  cout << endl;
  ReleaseMutex(hMutex);
}
// Find a pointer in gclist.
template <class T, int size>
typename list<GCInfo<T> >::iterator
  GCPtr<T, size>::findPtrInfo(T *ptr) {
  list<GCInfo<T> >::iterator p;
  // Find ptr in gclist.
  for(p = gclist.begin(); p != gclist.end(); p++)
    if(p->memPtr == ptr)
      return p;
  return p;
}
// Entry point for garbage collector thread.
template <class T, int size>
unsigned __stdcall GCPtr<T, size>::gc(void * param) {
  #ifdef DISPLAY
    cout << "Garbage collection started.\n";
  #endif
  while(isRunning()) {
      collect();
  }
  collect(); // collect garbage on way out
  // Release and reset the thread handle so
  // that the garbage collection thread can
  // be restarted if necessary.
  CloseHandle(hThrd);
  hThrd = 0;
  #ifdef DISPLAY
    cout << "Garbage collection terminated for "
         << typeid(T).name() << "\n";
  #endif
  return 0;
}
// Clear gclist when program exits.
template <class T, int size>
void GCPtr<T, size>::shutdown() {
  if(gclistSize() == 0) return; // list is empty
  list<GCInfo<T> >::iterator p;
  #ifdef DISPLAY
    cout << "Before collecting for shutdown() for "
         << typeid(T).name() << "\n";
  #endif
  for(p = gclist.begin(); p != gclist.end(); p++) {
    // Set all remaining reference counts to zero.
    p->refcount = 0;
  }
  collect();
  #ifdef DISPLAY
    cout << "After collecting for shutdown() for "
         << typeid(T).name() << "\n";
  #endif
}


blog comments powered by Disqus
C++ ARTICLES

- Intel Threading Building Blocks
- Threading Building Blocks with C++
- Video Memory Programming in Text Mode
- More Tricks to Gain Speed in Programming Con...
- Easy and Efficient Programming for Contests
- Preparing For Programming Contests
- Programming Contests: Why Bother?
- Polymorphism in C++
- Overview of Virtual Functions
- Inheritance in C++
- Extending the Basic Streams in C++
- Using Stringstreams in C++
- Custom Stream Manipulation in C++
- General Stream Manipulation in C++
- Serialize Your Class into Streams in C++

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2017 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials