I am trying to syncronize the queue between threads using mutex. I have 2 mil random integers in input.txt file. Main thread reads integers then puts them in queue. Other threads are simultaneously taking number from the queue and are doing stuff with it. I succeded to get accurate result each time I run program, but I think there is more efficient way to complete this task Code:
#include <iostream>
#include <thread>
#include <vector>
#include <utility>
#include <string>
#include <future>
#include <queue>
#include <random>
#include <chrono>
#include <sstream>
#include <fstream>
#include <iomanip>
template <typename T>
class SafeQ
{
private:
std::queue<T> q;
std::mutex mtx;
std::condition_variable cv;
public:
void push(T const& value)
{
std::unique_lock<std::mutex> lock(mtx);
q.push(value);
lock.unlock();
cv.notify_one();
}
void wait_and_pop(T& value)
{
std::unique_lock<std::mutex> lock(mtx);
while(q.empty())
{
cv.wait(lock);
}
value = q.front();
q.pop();
lock.unlock();
}
bool try_pop(T& popped_value)
{
std::unique_lock<std::mutex> lock(mtx);
if(q.empty())
{
return false;
}
popped_value = q.front();
q.pop();
return true;
}
size_t size()
{
std::unique_lock<std::mutex> lock(mtx);
size_t s = q.size();
return s;
}
bool empty()
{
std::lock_guard<std::mutex> lock(mtx);
return q.empty();
}
};
int producer(std::string filename, SafeQ<int> &q, bool &produce_finished)
{
int produced_count = 0;
std::ifstream ifs(filename);
while(!ifs.eof())
{
int num;
ifs >> num;
q.push(num);
produced_count++;
}
ifs.close();
produce_finished = true;
return produced_count;
}
void workerThread(SafeQ<int> &q, int &primes, int &nonprimes, double &sum, int &consumed_count, std::vector<int> &number_counts, bool produce_finished, std::mutex &m)
{
while(!q.empty() && !produce_finished)
{
int num;
q.wait_and_pop(num);
std::unique_lock<std::mutex> lock(m);
consumed_count++;
if(kernel(num) == 1) {
primes++;
}
else {
nonprimes++;
}
number_counts[num%10]++;
sum+=num;
lock.unlock();
}
}
int main(int argc, char **argv)
{
int num_threads = std::thread::hardware_concurrency();
std::mutex m;
bool no_exec_times = false, only_exec_times = false;;
std::string filename = "input.txt";
parse_args(argc, argv, num_threads, filename, no_exec_times, only_exec_times);
int consumed_count = 0;
bool produce_finished = false;
int primes = 0, nonprimes = 0, count = 0;
double mean = 0.0, sum = 0.0;
std::vector<int> number_counts(10, 0);
std::vector<std::thread> workers;
SafeQ<int> q;
std::future<int> f = std::async(std::launch::async, producer, filename, std::ref(q), std::ref(produce_finished));
for(int i = 0; i < num_threads; i++)
{
workers.push_back(std::thread(workerThread, std::ref(q), std::ref(primes), std::ref(nonprimes), std::ref(sum), std::ref(consumed_count), std::ref(number_counts), produce_finished, std::ref(m)));
}
for (int i = 0; i < num_threads; i++)
{
workers[i].join();
}
int produced_count = f.get();
mean = sum/consumed_count;
if ( produced_count != consumed_count ) {
std::cout << "[error]: produced_count (" << produced_count << ") != consumed_count (" << consumed_count << ")." << std::endl;
}
print_output(num_threads, primes, nonprimes, mean, number_counts);
return 0;
}
Is there way to upgrade this code using mutex and avoid using atomic?
bool kernel(int number)
{
bool prime = true;
for (int i = 2; i <= number / 2; ++i)
{
if (number % i == 0)
{
prime = false;
}
}
return prime;
};