Skip to content

Simple C++ multithreading manager with asynchronous callbacks

License

Notifications You must be signed in to change notification settings

deempalme/multithreading

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Torero multithreading manager

Simple C++ multithreading library to run functions in different CPU threads, check and perform callbacks when they are finished.

Several problems arise when you use multithreading; one of the most important is that you cannot call a function in the main thread from another function being executed in a different thread, if you do so, the secondary thread will create a copy of the function and execute it in that same secondary thread (even if you wanted to run it in the main thread). An alternative solution is to use join() and call the other function after is joined but, this has a big disadvantage; it will wait (froze the main thread execution) until the concurrent function is finished.

This library will allow you to run your functions concurrently and make callbacks in the main thread after they are finished. Therefore, you will not have to wait until they finish.


Table of Contents

1 Required components
    1.1 Installing Boost
2 Adding the library to your project
3 How of use
    3.1 Defining necessary variables
    3.2 Defining a run function
    3.3 Defining a callback function
    3.4 Creating the multithreading object
    3.5 Adding an execution in a new thread
    3.6 Checking if all concurrent functions have finished
    3.7 Updating ready callbacks
4 Example
    4.1 Building the example
5 Reference guide
    5.1 List of public member functions
    5.2 Public member functions
        5.2.1 Returning the total number of currently used threads
        5.2.2 Adding a new thread
        5.2.3 Getting variable that indicates if all threads have finished
        5.2.4 Returning the number of processes being executed concurrently
        5.2.5 Returning the number of processes awaiting for execution
        5.2.6 Returning the total number of threads in your computer
        5.2.7 Updating the state of concurrent threads


1 Required components

1.1 Installing Boost

Boost libraries must be installed. To do it in Ubuntu use the following commands in a terminal (your account need to have sudo permissions):

sudo apt-get install libboost-all-dev
sudo apt-get install aptitude
aptitude search boost

The installation in windows is a little more complicated, here is the official installation guide.

2 Adding the library to your project

You must copy this repository folder (or make a link [ubuntu] or shortcut [windows]) into your project's folder and add/modify the next lines in your CMakeLists.txt:

# Tripe points (...) represent possible content that may 
# already exist in your CMakeLists.txt
...
# Replace <multithreading_folder_path> with the actual folder path
add_subdirectory(<multithreading_folder_path>)
...
# Replace <project_name> with the actual project name
target_include_directories(<project_name>
  PRIVATE #or PUBLIC (depending in your preferences)
  ...
  ${TORERO_THREADS_DIRS}
  ...
)
...
target_link_libraries(<project_name>
  ...
  ${TORERO_THREADS_LIB}
  ...
)
...

3 How of use

3.1 Defining necessary variables

It is not really necessary to have a mutex that would lock all the variables inside the function that will be running in a different thread but, it is highly recommended to do so, and avoid reading a variable while is written in a different thread (or viceversa). Additionally, the mutex will allow this library to perform a callback when the execution of the concurrent function is finished. This step is really easy, just create a new boost::mutex object in your class or as a lone variable:

boost::mutex our_mutex;

3.2 Defining a run function

This is the function that will run in a separated thread, the returning type should be void, it could be a class member or a lone function, and it could have as many parameter as you wish. Remember that you must use the recently created boost::mutex object inside this function if you want to use the callback after this function finishes.

void test_run(const double parameter, const int &referenced_par, float *pointed_par){
  // Our boost::mutex object:
  our_mutex.lock();   // Locking all the variables inside this function

  ... // All the code that should be executed in a different thread

  our_mutex.unlock(); // Unlocking the variables
}

3.3 Defining a callback function

This is the function that will be executed after the "run function" is finished, its returning type should be void. It could be a class member or a lone function, and it could have as many parameter as you wish. Note: this is executed in the main thread.

void test_ready(const double parameter, const int &referenced_par, float *pointed_par){

  ... // Callback code

}

3.4 Creating the multithreading object

The creation of objects from this class is very easy, it does not require any additional parameter:

torero::cpu::threads thread_manager;

3.5 Adding an execution in a new thread

This function will add a new thread to execute concurrently or wait until at least one is free if all CPU threads are busy. It calls ready after run's execution is finished.

  // When using lone functions:
  thread_manager.multithread_add_process(
    boost::bind(test_run, parameter_variable_1, referenced_variable, &pointed_variable),
    boost::bind(test_ready, parameter_variable_1, referenced_variable_2, &pointed_variable_2),
    &our_mutex
  );

  // When using class member functions:
  your_class my_class;

  thread_manager.multithread_add_process(
    boost::bind(&your_class::test_run, &my_class, variable_1, referenced_var_1, &pointed_var_1),
    boost::bind(&your_class::test_ready, &my_class, variable_1, referenced_var_2, &pointed_var_2),
    &your_class.our_mutex
  );

  // If you don't want to use a boost::mutex nor the callback function use the following:
  thread_manager.multithread_add_process(
    boost::bind(test_run, parameter_variable_1, referenced_variable, &pointed_variable),
    0,
    nullptr
  );

For more info about boost::bind please visit here.

3.6 Checking if all concurrent functions have finished

To check if all concurrent executions that you created using multithread_add_process have finished, just use the following line:

// This will return `true` when all concurrent functions have finished
thread_manager.multithread_all_finished();

3.7 Updating ready callbacks

The following code line will check if functions running in another thread have finished, and execute their callbacks every time one or more conclude.

// Checking if threads have finished, if yes then, calls their ready function
thread_manager.multithread_update_process();

It is necessary to use that line to execute the callbacks. You could place it in your main loop so it will check it in every iteration. Example:

int main(int argc, char *argv[]){
  ...
  // This could be your main loop
  while(your_conditional){
    ...
    // Checking if the threads have finished, if yes then, calls their ready functions
    thread_manager.multithread_update_process();
    ...
  }
  ...
  return 0;
}

4 Example

There is a full running example in the folder examples.

4.1 Building the example

Open a terminal and go into the examples folder in this repository then, execute the following commands:

mkdir build
cd build
cmake ../
# -j8 is an argument to increase compilation speed, it selects how
# many processor's cores should be used; -j8 will utilize 8 cores, 
# MAKE SURE you DO NOT select more cores than what you actually have.
make -j8

To run the program execute this command:

./torero_thread_example

5 Reference guide

Main header:

Type Code
Header #include "torero/cpu/threads.h"

5.1 List of public member functions

Returns Member
unsigned int multithread_active_threads()
void multithread_add_process(boost::function run, boost::function ready, boost::mutex *mutex_pointer)
const bool& multithread_all_finished()
std::size_t multithread_number_of_active_processes()
std::size_t multithread_number_of_awaiting_processes()
unsigned int multithread_number_of_threads()
void multithread_update_process()

5.2 Public member functions

5.2.1 Returning the total number of currently used threads

Returns the number of threads that are currently being used by this multithreading manager.

unsigned int multithread_active_threads();

  Returns

Type Description
[unsigned int] Number of currently used threads in your CPU.

5.2.2 Adding a new thread

This function will add a new thread to execute concurrently or wait until at least one is free if all CPU threads are busy. It calls ready after run's execution is finished. Do not forget to include the pointer to the mutex that you use inside run's thread to check if the thread is finished (if not included then ready will never be called).

void multithread_add_process(boost::function<void (void)> run,
                             boost::function<void (void)> ready,
                             boost::mutex *mutex_pointer);

  Arguments

Type Name Description
[boost::function<void (void)>&] run Function to execute in concurrent thread.
[boost::function<void (void)>&] ready Callback function after thread is finished.
[boost::mutex*] mutex_pointer Is the pointer to the mutex that locks the variables inside the thread, use nullptr if you don't want any callback

5.2.3 Getting variable that indicates if all threads have finished

This function will return you a constant reference to a boolean variable that indicates if all created threads have been executed and finished. You can save this reference into a local variable and do easier checkings.

const bool &multithread_all_finished();

  Returns

Type Description
[const bool&] Returns the reference of boolean variable. true indicates that all threads have been executed and finished.

5.2.4 Returning the number of processes being executed concurrently

Returns the total number of active concurrent processes.

std::size_t multithread_number_of_active_processes();

  Returns

Type Description
[std::size_t] Number of active concurrent processes.

5.2.5 Returning the number of processes awaiting for execution

The total concurrent number of processes cannot be bigger than your number of threads, the manager will wait until there are free threads to execute the awaiting processes, this will return the total number of them.

std::size_t multithread_number_of_awaiting_processes();

  Returns

Type Description
[std::size_t] Number of awaiting concurrent processes.

5.2.6 Returning the total number of threads in your computer

Returns the number of threads in your CPU.

unsigned int multithread_number_of_threads();

  Returns

Type Description
[unsigned int] Number of threads in your CPU.

5.2.7 Updating the state of concurrent threads

This function will check if each created thread has been finished, and to make the correspodent callbacks when they are done. Call this function constanly after threads have been created until they all are finished, if not then, no callback will be made when a thread ends. It is recommended to call it in a time-lapse not too frequent, adequate may be around 30Hz.

void multithread_update_process();

Releases

No releases published