18.11 OBJECT-ORIENTED MULTITHREADING IN C


18.11 OBJECT-ORIENTED MULTITHREADING IN C++

While it is possible to use POSIX threads directly for C++ programs, there are now also available various object-oriented packages and stand-alone threading classes for this purpose. The ++ classes for threading are in most cases built on top of the POSIX threads, meaning that the threads created by the relevant C++ classes get compiled down into POSIX threads.

In this section, we will illustrate some of the C++ threading classes that come with the Qt library.[23] Qt provides a class QThread whose functionality is similar to the Java class Thread. A C++ class can be made multithreadable by extending QThread and overriding its run method. The code that needs to be executed in separate threads is placed in the run method. This code can be invoked for execution by calling the start method of QThread.

This is demonstrated by the following class, HelloThreadWithJoin, that gets executed in the same fashion as the Java class of the same name in Section 18.1. Note that what's accomplished by join for the Java case is accomplished by wait here. The thread in which main executes blocks when it encounters

     ht1.wait(); 

until the thread on which wait is invoked, in this case the thread ht1, has run to termination (or was never started in the first place). The wait also takes an optional argument consisting of time in milliseconds that if elapsed from the moment the function is invoked will cause wait to return. The same is the case with the invocation ht2. wait and ht3. wait in the program.

 
//HelloThreadWithJoin.cc #include <qthread.h> #include <string> #include <iostream> using namespace std; class HelloThread : public QThread { string message; public: HelloThread(string message) { this->message = message; } void run() { cout << message; } }; int main() { HelloThread ht1("Good"); HelloThread ht2("Morning"); HelloThread ht3("to"); ht1.start(); ht2.start(); ht3.start(); ht1.wait(); ht2.wait(); ht3.wait(); cout << "you!" << endl; return 0; }

This program can be compiled with the following command line:

     g++ -o HelloThreadWithJoin HelloThreadWithJoin.cc \         -I$QTDIR/include -DQT_THREAD_SUPPORT -L$QTDIR/lib -lqt-mt 

Vis-à-vis the command-line compilation of Qt programs in Chapter 17, note the use of the macro QT_THREAD-SUPPORT for including multithreading support at compile time. Also note that now we are linking the program with the library libqt-mt that is automatically created when you install Qt with the multithreading option activated.

Our next example illustrates the use of QMutex class for suppressing thread interference by using mutex locks. This is a C++ version of the Java program SynchedSwaps.java of Section 18.5. In the code shown below, a mutex lock is used in the function itemSwap; that ensures that only one thread at a time will gain access to the code inside itemSwap. The function keepBusy inside itemSwap is supposed to simulate the condition of possible additional computing between the time instants when the two int data members of the DataObject class are changed in equal but opposite manner.

 
//SynchedSwaps.cc #include <qthread.h> #include <cstdlib> #include <iostream> #include <ctime> using namespace std; void keepBusy(double howLongInMillisec); class DataObject : public QThread { QMutex mutex; int dataItem1; int dataItem2; public: DataObject() { dataItem1 = 50; dataItem2 = 50; } void itemSwap() { mutex.lock(); //(A) int x = (int) (-4.999999 + rand() % 10); dataItem1 -= x; keepBusy(1); dataItem2 += x; mutex.unlock(); //(B) } void test() { mutex.lock(); int sum = dataItem1 + dataItem2; cout << sum << endl; mutex.unlock(); } void run() {} }; DataObject dobj; class RepeatedSwaps : public QThread { public: RepeatedSwaps() { start(); } void run() { int i = 0; while (i++ < 5000) { dobj.itemSwap(); if (i % 1000 == 0) dobj.text(); } } }; int main() { RepeatedSwaps t0; RepeatedSwaps t1; RepeatedSwaps t2; RepeatedSwaps t3; t0.wait(); t1.wait(); t2.wait(); t3.wait(); } void keepBusy(double howLongInMillisec) { int ticksPerSec = CLOCKS_PER_SEC; int ticksPerMillisec = ticksPerSec / 1000; clock_t ct = clock(); while (clock() < ct + howLongInMillisec * ticksPerMillisec) ; }

With the mutex lock in place, the program produces the following output:

     100     100     100     100     100     100     100     100     100     100     100     100     100     100     100     100     100     100     100     100 

However, if you comment out the mutex locks in lines (A) and (B), the output becomes the following, clearly showing that thread interference is taking its toll:

     97     100     97     94     97     99     103     91     109     97     100     95     99     96     96     96     96     96     96     100 

Qt also comes with a class QWaitCondition that can be used to keep multiple threads from getting into deadlocks in the same manner as the wait-notify mechanism of Java and the wait-signal mechanism for POSIX threads. QWaitCondition comes with the member function wait that works in a manner analogous to Object.wait for Java. When a thread executes this wait, the thread releases the mutex that must be locked prior to the invocation of wait. Such a thread stays dormant until another thread executes either wakeOne or wakeAll, the former for notifying one of the randomly selected waiting threads and the latter for notifying all waiting threads. The following program illustrates the use of the wait-wakeAll mechanism provided by the QWaitCondition class. This program is a C++ version of the MultiCustomerAccount.java program shown earlier in Section 18.6. We have five depositor threads and five withdrawer threads. If a withdrawer thread wishes to withdraw an amount that exceeds the current balance in the account, the thread must wait until the depositor threads have put sufficient money into the account.

 
//MultiCustomerAccount.cc #include <qthread.h> #include <cstdlib> #include <iostream> #include <ctime> using namespace std; void keepBusy(double howLongInMillisec); QMutex mutex; QWaitCondition cond; class Account : public QThread { public: int balance; Account() { balance = 0; } void deposit (int dep) { mutex.lock(); balance += dep; keepBusy(1); cond.wakeAll(); mutex.unlock(); } void withdraw(int draw) { mutex.lock(); while (balance < draw) { cond.wait (&mutex); } keepBusy(1); balance -= draw; mutex.unlock(); } void run(){} }; Account acct; class Depositor : public QThread { public: void run() { int i = 0; while (true) { int x = (int) (rand() % 10); acct.deposit(x); if (i++ % 100 == 0) cerr << "balance after deposits:" << acct.balance << endl; keepBusy(1); } } }; class Withdrawer : public QThread { public: void run() { int i = 0; while (true) { int x = (int) (rand() % 10); acct.withdraw(x); if (i++ % 100 == 0) cerr << "balance after withdrawals:" << acct.balance << endl; keepBusy(1); } } }; int main { Depositor* depositors[5]; Withdrawer* withdrawers[5]; for (int i=0; i < 5; i++) { depositors[ i ] = new Depositor(); withdrawers[ i ] = new Withdrawer(); depositors[ i ]->start(); withdrawers[ i ]->start(); } for (int i=0; i < 5; i++) { depositors[ i ]->wait(); withdrawers[ i ]->wait(); } } void keepBusy(double howLongInMillisec) { int ticksPerSec = CLOCKS_PER_SEC; int ticksPerMillisec = ticksPerSec / 1000; clock_t ct = clock(); while (clock() < ct + howLongInMillisec * ticksPerMillisec) ; }

[23]For the examples shown in this section to work, you may have to reinstall Qt on your system if the original installation (for the GUI work in Chapter 17) was carried out without activating the support for multithreading. To reinstall Qt, first uninstall the existing version by going into the main Qt directory and executing

 make clean 

and then reconfigure and rebuild Qt by

 configure -thread 

make




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

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