// // Program to add numbers (read in from file, or generated // randomly). // // Command-line arguments: // (1) number of threads to use (P) // (2) name of file containing numbers (doubles) // or "-n N" to randomly generate N doubles. // // In this version, we use classes to hide more of the low-level // details of multithreading and avoid global variables. // #include #include #include #include // has setprecision() #include // has exit(), etc. #include "pthreads-threadmgr.h" // has threadManager class #include "pthreads-lock.h" // has lockObj class #include "timer.h" // has timer() // ---- Class for computing sum--------------------------------------- // This will be an "enclosing class" to hold variables to be shared // among threads. We'll build one object of this class, and pass // a pointer to it to all the threads. This seems tidier than // either of the previous approaches (global variables, or static // class variables). This class will also provide methods that // do the actual desired work, including starting up threads and // waiting for them to complete. class sumObj { public: // Constructor to get input from file. sumObj(const int nThreads, const char * fileName); // Constructor to generate input randomly. sumObj(const int nThreads, const int nNums); // Destructor. ~sumObj(void); // Do calculation. void calculate(void); // Print result. void print(void); private: // These are the variables to be shared among threads. int P; // number of threads int N; // number of numbers double * nums; // numbers to sum double sum; lockObj sumLock; // lock for sum // ---- Class for thread ------------------------------------------- class sumThreadObj { public: typedef sumThreadObj * ptr; sumThreadObj(const int myID, sumObj * context); void run(void); private: int myID; // unique ID for thread sumObj * context; // pointer to "enclosing" object }; }; // ---- Main program ------------------------------------------------- int main(int argc, char* argv[]) { // Process command-line arguments. if (argc < 3) { cerr << "Usage: " << argv[0] << " numThreads [inputfile | -n N]\n"; exit(EXIT_FAILURE); } sumObj *obj; // object that will hold data and // do work int P = atoi(argv[1]); // number of threads // Time before initialization. double startInitTime = timer(); if (argc == 3) obj = new sumObj(P, argv[2]); else obj = new sumObj(P, atoi(argv[3])); // Time after initialiation and before compute. double startTime = timer(); // Calculate sum. obj->calculate(); // Time after compute. double endTime = timer(); // Print results. obj->print(); cout << "Time to compute sum (seconds) = " << setprecision(4) << endTime - startTime << endl; cout << "Total time (seconds) = " << setprecision(4) << endTime - startInitTime << endl; // Clean up and exit. delete obj; return EXIT_SUCCESS; } // ---- Functions, etc., for sumObj class -------------------------- // Constructor to get input from file. sumObj::sumObj(const int nThreads, const char * filename) { P = nThreads; ifstream infile; infile.open(filename); if (!infile) { cerr << "Input file " << filename << " not found\n"; exit(EXIT_FAILURE); } double temp; vector vTemp; while (infile >> temp) vTemp.push_back(temp); infile.close(); N = vTemp.size(); nums = new double[N]; copy(vTemp.begin(), vTemp.end(), nums); } // Constructor to generate input randomly. sumObj::sumObj(const int nThreads, const int nNums) { P = nThreads; N = nNums; nums = new double[N]; for (int i = 0; i < N; ++i) nums[i] = (double) rand() / (double) (RAND_MAX); } // Destructor. sumObj::~sumObj(void) { delete [] nums; } // Do calculation. void sumObj::calculate(void) { sum = 0.0; // Create P sumThreadObj objects, one for each thread, to // hold parameters. sumThreadObj::ptr * threadObjs = new sumThreadObj::ptr[P]; for (int i = 0; i < P; ++i) threadObjs[i] = new sumThreadObj(i, this); // Create and start P new threads. threadManager tMgr(P, threadObjs); // Wait for all threads to complete. tMgr.join(); // Clean up. for (int i = 0; i < P; ++i) delete threadObjs[i]; delete [] threadObjs; } // Print result. void sumObj::print(void) { cout << "Sum = " << setprecision(10) << sum << endl; cout << "Added " << N << " numbers using " << P << " threads\n"; } // ---- Functions, etc., for sumObj::sumThreadObj class ------------ // Constructor. sumObj::sumThreadObj::sumThreadObj(const int myID, sumObj * context) { this->myID = myID; this->context = context; } // Member function containing code each thread should execute. void sumObj::sumThreadObj::run(void) { // Initialization -- make local copies of some shared variables. int N = context->N; int P = context->P; double * nums = context->nums; int numsPerThread = (N+P-1)/P; int first = myID * numsPerThread; int limit = first + numsPerThread; if (limit > N) limit = N; // Calculate partial sum. double partialSum = 0; for (int i = first; i < limit; ++i) partialSum += nums[i]; // Add to global sum (using lock to be sure only one thread // at a time performs this operation). context->sumLock.lock(); context->sum += partialSum; context->sumLock.unlock(); }