Threaded pi calculation with concurrent queue (C++)

Code

#include "tbb/concurrent_queue.h"

#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>

class Logger
{
public:
    static void log(const std::string& msg)
    {
        static std::mutex logMutex;
        std::lock_guard<std::mutex> lock(logMutex);
        std::cout << msg << std::endl;
    }
};

class PiCalculator
{
public:
    PiCalculator()
    : m_stopRequested(false), m_cv_mutex(), m_queue(), m_condVar(),
      m_calcCount(0), m_worker(std::thread([this](){run();})) {}

    ~PiCalculator()
    {
        if (m_worker.joinable())
        {
            requestStop();
            m_worker.join();
        }
    }

    void addCalculation(uint64_t iter)
    {
        m_queue.push(iter);
        m_condVar.notify_all();
    }

private:
    void requestStop()
    {
        m_stopRequested = true;
        m_condVar.notify_all();
    }

    void run()
    {
        uint64_t iter = 0;
        uint32_t calcNum = 0;

        // Worker thread - Gets work from the task queue

        while (!m_stopRequested)
        {
            std::unique_lock<std::mutex> cv_lock(m_cv_mutex);
            m_condVar.wait(cv_lock, [this] {
                return m_stopRequested || !m_queue.empty();
            });

            if (!m_stopRequested && m_queue.try_pop(iter))
            {
                calcNum = m_calcCount++;
                calculate(iter, calcNum);
            }
        }
    }

    void calculate(uint64_t iter, uint32_t calcNum)
    {
        // Worker thread - Executes a possibly long time task

        auto calcNumStr = std::to_string(calcNum);
        Logger::log("[" + calcNumStr + "]" + " Start calculation ...");

        double sum = 0.0;
        int32_t sign = 1;

        for (uint64_t i = 0; !m_stopRequested && i < iter; ++i)
        {
            sum += sign / (2.0 * i + 1.0);
            sign *= -1;
        }

        auto piStr = std::to_string(4.0 * sum);
        Logger::log("[" + calcNumStr + "]" + " ... finished: " + piStr);
    }

private:
    std::atomic<bool> m_stopRequested;
    tbb::concurrent_queue<uint64_t> m_queue;
    std::mutex m_cv_mutex;
    std::condition_variable m_condVar;
    std::atomic<uint32_t> m_calcCount;
    std::thread m_worker;
};

int main()
{
    // Main thread - Sets up the program

    Logger::log("Calculating Pi with the Taylor method");
    Logger::log("");
    Logger::log("Possible Input:");
    Logger::log("- <num> ... number of iterations");
    Logger::log("- quit  ... quits the process");
    Logger::log("");

    std::string input = "";
    PiCalculator piCalculator;

    // Main thread - Enters the main event loop

    while (true)
    {
        std::cin >> input;

        if (input == "quit") { break; }

        try
        {
            uint64_t iter = std::stoull(input);
            piCalculator.addCalculation(iter);
        }
        catch (...)
        {
            Logger::log("The input was invalid");
        }
    }

    return 0;
}
$ g++ main.cpp -ltbb -lpthread

Future

  • Switch to a thread pool to execute the different calculations in parallel.