#include #include #include #include #include #include #include #include #include #include using namespace std; using RangeT = pair; // the queue and a way to lock it. queue Q; mutex Q_Mutex; condition_variable QConditionVar; // an active thread count and a way to lock that. size_t thread_count{0}; mutex Thread_Count_Mutex; // a way to stop computation. atomicstop_all_threads{false}; const int DATA_SIZE{10'000'000}; const int MAX_ELEMENT{10'000'000}; vector sortData; void SelectionSort(int low, int high) { size_t i; int small; for(i=low; i < high; ++i) { small = i; for(size_t j = i+1; j <= high; ++j) { if (sortData[j] < sortData[small]) { small = j; } } if (small != i) { swap(sortData[i],sortData[small]); } } } int Partition(int low, int high) { int pivot{sortData[high]}; int i{low -1}; for(int j = low; j < high; ++j) { if (sortData[j] <= pivot) { ++i; swap(sortData[i], sortData[j]); } } ++i; swap(sortData[i], sortData[high]); return i; } void QSort(RangeT range) { int low{range.first}, high{range.second}; int mid; if(low < high) { if (high - low < 10) { SelectionSort(low, high); } else { mid = Partition(low, high); if (low < mid-1) { lock_guard guard(Q_Mutex); Q.push(RangeT(low, mid-1)); } if (high > mid+1) { lock_guard guard(Q_Mutex); Q.push(RangeT( mid+1, high)); } } } } RangeT GetRange() { RangeT r; unique_lock lock{Q_Mutex}; while(Q.size() == 0 and not stop_all_threads) { QConditionVar.wait(lock); } // to make the exit condition work. // I have the queue locked. // I need to increment the thread count // at the same time as I remove the entry from the queue. if (not stop_all_threads) { Thread_Count_Mutex.lock(); r = Q.front(); thread_count ++; Q.pop(); Thread_Count_Mutex.unlock(); } return r; } void ThreadFunction (int id){ bool done{false}; while (not stop_all_threads) { RangeT r{GetRange()}; if (not stop_all_threads) { QSort(r); Thread_Count_Mutex.lock(); thread_count --; Thread_Count_Mutex.unlock(); } // ok so I am out. Grab a mutex on the threads an on the queue. Q_Mutex.lock(); Thread_Count_Mutex.lock(); if (thread_count == 0 and Q.size() == 0) { stop_all_threads = true; } Thread_Count_Mutex.unlock(); Q_Mutex.unlock(); QConditionVar.notify_all(); } return ; } int main(int argc, char * argv[]) { vector threads; // vector check; int threadCount {8}; if (argc > 1) { threadCount = atoi(argv[1]); if (threadCount < 1 or threadCount > 100) { threadCount = 8; } } for(size_t i =0; i < DATA_SIZE; i++) { int tmp {rand() % MAX_ELEMENT+1}; sortData.push_back(tmp); // check.push_back(tmp); } //sort(check.begin(), check.end()); auto start = chrono::system_clock::now(); Q.push(RangeT(0, DATA_SIZE-1)); cout << "Starting " << threadCount << " threads" << endl; for(int i = 0; i < threadCount; ++i) { thread t(ThreadFunction, i); threads.push_back(move(t)); } QConditionVar.notify_all(); for( int i = 0; i < threadCount; ++i) { threads[i].join(); } auto end = chrono::system_clock::now(); chrono::duration totalTime = end-start; cout << "The wall clock time used is " << totalTime.count() << " seconds" << endl; /* for(int i = 0; i < check.size();++i) { if (sortData[i] != check[i]) { cout << "Error at position " << i << endl; cout << "\t" << sortData[i] << " != " << check[i] << endl; } } */ return 0; }