In this part of my C++ Tutorial I’ll be focusing on Threads. We’ll cover how to create, pause, and pool threads. We will then compare how long it takes to generate a list of prime numbers with and without threads. Also we’ll check out many ways of working with time.
Like always the heavily commented code follows the video below. Feel free to do whatever you’d like with it.
If you like videos like this, consider donating $1, or simply turn off Ad Blocking software. Either helps me to continue making free educational video tutorials.
Code from the Video
// ---------- C++ TUTORIAL 16 ----------
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include <ctime>
#include <numeric>
#include <cmath>
#include <sstream>
#include <thread>
#include <chrono>
#include <ctime>
#include <mutex>
int GetRandom(int max){
srand(time(NULL));
return rand() % max;
}
/* ----- SIMPLE THREAD EXAMPLE -----
void ExecuteThread(int id){
// Get current time
auto nowTime = std::chrono::system_clock::now();
// Convert to a time we can output
std::time_t sleepTime =
std::chrono::system_clock::to_time_t(nowTime);
// Convert to current time zone
tm myLocalTime = *localtime(&sleepTime);
// Print full time information
std::cout << "Thread " << id <<
" Sleep Time : " <<
std::ctime(&sleepTime) << "\n";
// Get separate pieces
std::cout << "Month : " <<
myLocalTime.tm_mon << "\n";
std::cout << "Day : " <<
myLocalTime.tm_mday << "\n";
std::cout << "Year : " <<
myLocalTime.tm_year + 1900 << "\n";
std::cout << "Hours : " <<
myLocalTime.tm_hour << "\n";
std::cout << "Minutes : " <<
myLocalTime.tm_min << "\n";
std::cout << "Seconds : " <<
myLocalTime.tm_sec << "\n\n";
// Put the thread to sleep for up to 3 seconds
std::this_thread::sleep_for (std::chrono::seconds(GetRandom(3)));
nowTime = std::chrono::system_clock::now();
sleepTime =
std::chrono::system_clock::to_time_t(nowTime);
std::cout << "Thread " << id <<
" Awake Time : " <<
std::ctime(&sleepTime) << "\n";
}
----- END SIMPLE THREAD EXAMPLE ----- */
std::string GetTime(){
auto nowTime = std::chrono::system_clock::now();
std::time_t sleepTime =
std::chrono::system_clock::to_time_t(nowTime);
return std::ctime(&sleepTime);
}
double acctBalance = 100;
// Protects shared data from being accessed at the
// same time
std::mutex acctLock;
void GetMoney(int id,
double withdrawal){
// The exception safe way to protect access
// to code within its scope. The lock is released
// after execution leaves this scope
std::lock_guard<std::mutex> lock(acctLock);
// Blocks access between lock and unlock
// until execution completes
// This isn't good to use however if an error
// occurs between lock and unlock
// acctLock.lock();
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << id <<
" tries to withdrawal $" <<
withdrawal << " on " <<
GetTime() << "\n";
if((acctBalance - withdrawal) >= 0){
acctBalance -= withdrawal;
std::cout << "New Account Balance is $" <<
acctBalance << "\n";
} else {
std::cout << "Not Enough Money in Account\n";
std::cout << "Current Balance is $" <<
acctBalance << "\n";
}
// acctLock.unlock();
}
int main()
{
/* ----- SIMPLE THREAD EXAMPLE -----
// Create a thread and pass a parameter
// to the function
std::thread th1 (ExecuteThread, 1);
// Join the thread to the main thread
// meaning main waits for this thread to
// stop executing before continuing execution
// of code in main
th1.join();
std::thread th2 (ExecuteThread, 2);
th2.join();
----- END SIMPLE THREAD EXAMPLE ----- */
// We will create a pool of threads that
// will access a bank account in no particular
// order
std::thread threads[10];
for(int i = 0; i < 10; ++i){
threads[i] = std::thread(GetMoney, i, 15);
}
for(int i = 0; i < 10; ++i){
threads[i].join();
}
return 0;
}
// ---------- CALCULATE PRIMES WITHOUT THREADS -----------
void FindPrimes(unsigned int start,
unsigned int end,
std::vector<unsigned int>& vect){
// Cycle through numbers while ignoring evens
for(unsigned int x = start; x <= end; x += 2){
for(unsigned int y = 2; y < x; y++){
if((x % y) == 0){
break;
} else if((y + 1) == x){
vect.push_back(x);
}
}
}
}
int main()
{
std::vector<unsigned int> primeVect;
// Get time before code starts executing
int startTime = clock();
FindPrimes(1, 100000, primeVect);
for(auto i: primeVect)
std::cout << i << "\n";
// Get time after execution
int endTime = clock();
// Print out the number of seconds by taking the difference
// and dividing by the clock ticks per second
std::cout << "Execution Time : " <<
(endTime - startTime)/double(CLOCKS_PER_SEC)
<< std::endl;
return 0;
}
// ---------- END CALCULATE PRIMES WITHOUT THREADS -----------
// ---------- CALCULATE PRIMES WITH THREADS -----------
// Used to protect writing to the vector
std::mutex vectLock;
std::vector<unsigned int> primeVect;
void FindPrimes(unsigned int start,
unsigned int end){
// Cycle through numbers while ignoring evens
for(unsigned int x = start; x <= end; x += 2){
// If a modulus is 0 we know it isn't prime
for(unsigned int y = 2; y < x; y++){
if((x % y) == 0){
break;
} else if((y + 1) == x){
vectLock.lock();
primeVect.push_back(x);
vectLock.unlock();
}
}
}
}
void FindPrimesWithThreads(unsigned int start,
unsigned int end,
unsigned int numThreads){
std::vector<std::thread> threadVect;
// Divide up the calculation so each thread
// operates on different primes
unsigned int threadSpread = end / numThreads;
unsigned int newEnd = start + threadSpread - 1;
// Create prime list for each thread
for(unsigned int x = 0; x < numThreads; x++){
threadVect.emplace_back(FindPrimes,
start, newEnd);
start += threadSpread;
newEnd += threadSpread;
}
for(auto& t : threadVect){
t.join();
}
}
int main()
{
// Get time before code starts executing
int startTime = clock();
FindPrimesWithThreads(1, 100000, 3);
// Get time after execution
int endTime = clock();
for(auto i: primeVect)
std::cout << i << "\n";
// Print out the number of seconds
std::cout << "Execution Time : " <<
(endTime - startTime)/double(CLOCKS_PER_SEC)
<< std::endl;
return 0;
}
// ---------- END CALCULATE PRIMES WITH THREADS -----------
// ---------- END C++ TUTORIAL 16 ----------