Developer documentation
Version 3.0.3-105-gd3941f44
Basic multi-threading primitives

basic functions and classes to allow multi-threading More...

Enumerations

enum class  MR::Thread::nthreads_t { MR::Thread::nthreads_t::UNINITIALISED , MR::Thread::nthreads_t::EXPLICIT , MR::Thread::nthreads_t::IMPLICIT }
 

Functions

size_t MR::Thread::number_of_threads ()
 
nthreads_t MR::Thread::type_nthreads ()
 
size_t MR::Thread::threads_to_execute ()
 
template<class Functor >
__Multi< typename std::remove_reference< Functor >::type > MR::Thread::multi (Functor &&functor, size_t nthreads=threads_to_execute())
 used to request multiple threads of the corresponding functor More...
 
template<class Functor >
__run< Functor >::type MR::Thread::run (Functor &&functor, const std::string &name="unnamed")
 Execute the functor's execute method in a separate thread. More...
 

Detailed Description

basic functions and classes to allow multi-threading

These functions and classes mostly provide a thin wrapper around the C++11 threads API. While they can be used as-is to develop multi-threaded applications, in practice the Thread-safe image looping and Thread-safe queue APIs provide much more convenient and powerful ways of developing robust and efficient applications.

Enumeration Type Documentation

◆ nthreads_t

enum class MR::Thread::nthreads_t
strong

provides information regarding whether the number of threads has been initialised, set explicitly, or determined implicitly. This may affect how particular algorithms choose to launch threads depending on the presence of a user request.

Enumerator
UNINITIALISED 
EXPLICIT 
IMPLICIT 

Definition at line 264 of file thread.h.

Function Documentation

◆ multi()

template<class Functor >
__Multi< typename std::remove_reference< Functor >::type > MR::Thread::multi ( Functor &&  functor,
size_t  nthreads = threads_to_execute() 
)
inline

used to request multiple threads of the corresponding functor

This function is used in combination with Thread::run or Thread::run_queue to request that the functor object be run in parallel using number threads of execution (defaults to Thread::threads_to_execute()).

See also
Thread::run()
Thread::run_queue()

Definition at line 285 of file thread.h.

◆ number_of_threads()

size_t MR::Thread::number_of_threads ( )

the number of cores available for multi-threading, as specified in the variable NumberOfThreads in the MRtrix configuration file, or set using the -nthreads command-line option

◆ run()

template<class Functor >
__run< Functor >::type MR::Thread::run ( Functor &&  functor,
const std::string &  name = "unnamed" 
)
inline

Execute the functor's execute method in a separate thread.

Launch a thread by running the execute method of the object functor, which should have the following prototype:

class MyFunc {
public:
void execute ();
};

The thread is launched by the constructor, and the destructor will wait for the thread to finish. The lifetime of a thread launched via this method is therefore restricted to the scope of the returned object. For example:

class MyFunctor {
public:
void execute () {
...
// do something useful
...
}
};
void some_function () {
MyFunctor func; // parameters can be passed to func in its constructor
// thread is launched as soon as my_thread is instantiated:
auto my_thread = Thread::run (func, "my function");
...
// do something else while my_thread is running
...
// wait for my_thread to complete - this is necessary to catch
// exceptions - see below
my_thread.wait();
}
__run< Functor >::type run(Functor &&functor, const std::string &name="unnamed")
Execute the functor's execute method in a separate thread.
Definition: thread.h:373

It is also possible to launch an array of threads in parallel, by wrapping the functor into a call to Thread::multi(), as follows:

...
auto my_threads = Thread::run (Thread::multi (func), "my function");
...
my_thread.wait();
...
__Multi< typename std::remove_reference< Functor >::type > multi(Functor &&functor, size_t nthreads=threads_to_execute())
used to request multiple threads of the corresponding functor
Definition: thread.h:285
Exception handling

Proper handling of exceptions in a multi-threaded context is non-trivial, and in general you should take every precaution to prevent threads from throwing exceptions. This means you should perform all error checking within a single-threaded context, before starting processing-intensive threads, so as to minimise the chances of anything going wrong at that stage.

In this implementation, the wait() function can be used to wait until all threads have completed, at which point any exceptions thrown will be displayed, and a futher exception re-thrown to allow the main application to catch the error (this could be the same exception that was originally thrown if a single thread was run). This means the application will continue processing if any of the remaining threads remain active, and it may be a while before the application itself is allowed to handle the error appropriately. If this behaviour is not appropriate, and you expect exceptions to be thrown occasionally, you should take steps to handle these yourself (e.g. by setting / checking some flag within your threads, etc.).

Note
while the wait() function will also be invoked in the destructor, any exceptions thrown will be caught and not re-thrown (throwing in the destructor is considered bad practice). This is to prevent undefined behaviour (i.e. crashes) when multiple thread objects are launched within the same scope, each of which might throw. In these cases, it is best to explicitly call wait() for each of the objects returned by Thread::run(), rather than relying on the destructor alone (note Thread::Queue ThreadedLoop already do this).
See also
Thread::multi()

Definition at line 373 of file thread.h.

◆ threads_to_execute()

size_t MR::Thread::threads_to_execute ( )

the number of threads to execute for a particular task if some higher-level process has already executed multiple threads, do not want the lower-level process querying this function to also generate a large number of threads; instead the lower-level function should run explicitly single-threaded.

◆ type_nthreads()

nthreads_t MR::Thread::type_nthreads ( )