Thread worker pools

x:run() starts a new thread immediately. A thread worker pool starts new threads in advance, and assigns them to a particular job, with a queue for waiting requests.

A x::workerpool is an object that has its own run() method that works generally the same way as x::run(), however a number of threads get started in advance (actually, the first time its run() method gets called):

#include <x/ref.H>
#include <x/obj.H>
#include <x/threads/workerpool.H>

class spaceshipObj : virtual public x::obj {

public:
    spaceshipObj() {}
    ~spaceshipObj() noexcept {}

    void run(int x, int y);
};

typedef x::workerpool<spaceshipObj> fleet_t;

fleet_t fleet=fleet_t::create(4, 8, "starship", L"army");

// ...

fleet->run(100, 200);

The worker pool template class takes a class name parameter that defines which classes get instantiated for each thread in the worker pool. The worker pool constructor takes two required parameters: the minimum number of threads that get started, and the maximum number of threads. The worker pool object will adjust the number of running threads, as needed. An instance of the template class gets constructed and destroyed, as the threads get created and destroyed. In this example, between four and eight threads, and instances of spaceshipObj get instantiated. The template class must have a default constructor.

The third and the fourth parameters are optional. The third parameter set the name of each running thread, for logging purposes. The fourth parameter sets the name of properties that specify the worker pool parameters: property::min, property::max, and property::name. The above example uses three properties, army::min, army::max, and army::name, to set the minimum and the maximum number of threads, and their names. The parameters to the constructor serve as the default value of the corresponding property. In absence of setting those properties, the parameters to the constructor take effect by default.

Invoking the worker pool object's run() forwards this call, and its parameters (which must be copy-constructible), to the first available spaceshipObj instance/thread. When the object's run() returns, the thread goes back in line waiting for another call to run(), or the thread may get terminated, if the thread's services are not required any more.

run() always returns immediately. If all threads are busy, the request goes into an internal queue, and goes to some thread when it becomes available.

When the last reference to the worker pool object itself goes out of scope, and it gets destroyed, the worker pool object's destructor stops any threads that are still running. If a thread is in a middle of a run() call, the destructor waits unil the thread returns, and terminates. If there were calls to the worker pool object's run() method that were never forwarded to a thread, because they were all busy, before the worker pool object got destroyed, those calls will never occur (and the internal queue that holds run()'s parameters gets destroyed, destroying each passed parameter along with it).

A simple worker pool thread

#include <x/ref.H>
#include <x/obj.H>
#include <x/threads/workerpool.H>

class spaceshipObj : virtual public x::obj {

public:
    spaceshipObj() {}
    ~spaceshipObj() noexcept {}

    void run(int x, int y);
};

typedef x::ref<spaceshipObj> spaceship;

typedef x::workerpool<> fleet_t;

spaceship enterprise=spaceship::create();

fleet_t fleet=fleet_t::create(4, 8, "starship", L"army");

// ...

fleet->run(enterprise, 100, 200);

The template class parameter to x::workerpool is optional. The default thread worker pool object implementation defines a run() method that takes at least one parameter. Its first parameter is any reference-counted object, with run() forwarding any remaining parameters to the object's own run() method.

The above example constructs a worker pool with a default thread class parameter. This class is simply this:

class x::simpleWorkerThreadObj {

public:

    template<typename objClass, typename baseClass, typename ...Args>
    inline void run(const ref<objClass, baseClass> &obj,
        Args && ...args)
    {
        obj->run(std::forward<Args>(args)...);
    }

    template<typename objClass, typename baseClass, typename ...Args>
    inline void run(const const_ref<objClass, baseClass> &obj,
        Args && ...args)
    {
        obj->run(std::forward<Args>(args)...);
    }
};

The call to the worker pool object's run() method passes a reference to a x::spaceshipObj as the first parameter. The default implementation of a worker pool thread class forwards its parameters to the referenced object's own run().

This approach is desirable when a worker pool thread class does not have a default constructor. The worker pool is now simply a means of limiting the maximum number of running threads, at the same time.

Note

A reference to the same object may be passed to multiple calls to run(). The referenced object's run() method may get executed by the same thread, or by different threads at the same time.