2.4.12. count_atomic.cpp#

2.4.12.1. Source code#

  1// University of Florida EEL6528
  2// Tan F. Wong
  3// Jan 14, 2021
  4
  5#include <uhd/utils/safe_main.hpp>
  6#include <uhd/utils/thread.hpp>
  7#include <boost/program_options.hpp>
  8#include <atomic>
  9#include <mutex>
 10#include <thread>
 11#include <chrono>
 12#include <csignal>
 13#include <sstream>
 14 
 15namespace po = boost::program_options;
 16
 17// Interrupt signal handlers
 18static bool stop_signal_called = false;
 19void sig_int_handler(int) {
 20    stop_signal_called = true;
 21}
 22
 23// Internal locking
 24class SharedPrinter {
 25  private:
 26    std::mutex mtx;
 27  public:
 28    void print(std::string message) {
 29        mtx.lock();
 30        std::cout << message;
 31        mtx.unlock();
 32    }
 33}; 
 34
 35// worker function that waits for its turn and then increments count
 36void wait_and_count(int id, int n, std::atomic<unsigned long>& count, SharedPrinter& printer) {
 37    while (not stop_signal_called) {
 38        if ((count.load()/10)%n == id) {
 39            // It is this thread's turn 
 40            std::ostringstream message;
 41            message << "Thread #" << id << " increments count to " << ++count << std::endl;
 42            printer.print(message.str());
 43            std::this_thread::sleep_for(std::chrono::seconds(1)); // Just don't go too fast
 44        } else {
 45            // Not this thread's turn
 46            std::this_thread::yield();
 47        }
 48    }
 49}
 50
 51int UHD_SAFE_MAIN(int argc, char *argv[]) {
 52    // Set main thread priority to highest
 53    uhd::set_thread_priority_safe(1.0, true);
 54 
 55    //variables to be set by po
 56    int num_threads;
 57 
 58    //setup the program options
 59    po::options_description desc("Allowed options");
 60    desc.add_options()
 61        ("help", "help message")
 62        ("num-threads,n", po::value<int>(&num_threads)->default_value(10), "Number of threads")
 63    ;
 64 
 65    po::variables_map vm;
 66    po::store(po::parse_command_line(argc, argv, desc), vm);
 67    po::notify(vm);
 68 
 69    //print the help message
 70    if (vm.count("help")) {
 71        std::cout << desc << std::endl;
 72        return EXIT_SUCCESS;
 73    }
 74
 75    std::signal(SIGINT, &sig_int_handler);
 76
 77    // Instantiate the SharedPrinter object
 78    SharedPrinter printer;
 79    
 80    // Initialize atomic varaible storing the count
 81    std::atomic<unsigned long> count(0);
 82
 83    // Spawn num_threads worker threads
 84    std::thread worker[num_threads];
 85    for (int i=0; i<num_threads; i++) {
 86        worker[i] = std::thread(&wait_and_count, i, num_threads, std::ref(count), std::ref(printer));
 87    }
 88        
 89    // Keep running until CTRL-C issued
 90    while (not stop_signal_called) {
 91        std::this_thread::yield(); // This is more CPU efficient
 92    }
 93
 94    // Join all worker threads
 95    for (int i=0; i<num_threads; i++) {
 96        if (worker[i].joinable()) worker[i].join();
 97    }
 98
 99    std::cout << std::endl;
100    return EXIT_SUCCESS;
101}

Experiment

  1. Study the source code . Compile and run it. Can you explain what the code does?

  2. Why do we still need to use the mutexed SharedPrinterobject here?

  3. Note the use of std::this_thread::yield() and the way of passing references in the function inside the std::thread constructor.