user997112
user997112

Reputation: 30615

C++ threading model for creating multiple instances of class spawning threads

Abstract:

I am designing a class (Inner) which spawns 2 threads- a producer and a consumer. In one usage there is one instance and in another context there are multiple instances.

In Standalone I need the two threads to continue writing/reading messages. However, if there's multiple instances I need the code to spawn the two threads but continue to create the next class (to spawn more threads).

My problem is trying to combine these two scenarios with correct usage of std::thread::join() and std::thread::detach().

Code details:

The class Inner spawns a thread to receive and queue messages and a second thread to read the queue and send messages to the class owning Inner.

template<Owner>
class Inner
{
    Inner(Owner& owner) : _owner(owner)
    {
        // Spawn thread to receive packets and put on queue
        // Spawn thread to read from queue
    }

    void receiveMessage(const Message& msg)
    {
        // Ommitted locks etc for simplicty
        _queue.push(msg);
    }

    void readFromQueue()
    {
        // Ommitted loop, locks etc for simplicty
        _owner.receiveMessage(msg);
    }

    Owner& _owner;
    std::queue<Message> _queue;
};

There are two possible owner classes, SingleInner:

class OneInner
{
    OneInner()
    {
        _inner = std::make_unique<Inner>();
    }

    void receiveMessage(const Message& msg){//Code ommitted}

    std::unique_ptr<Inner> _inner;
};

and the second context has multiple instances:

class MultipleInners
{
    MultipleInners()
    {
        // Need to create multiple instances of Inner, each with Inner's two threads running
    }

    void receiveMessage(const Message& msg){//Code ommitted}

    std::vector<std::unique_ptr<Inner>> _inners;
};

I'm unsure how I can allow Inner to spawn 2 threads, have them keep running but in OneInner the code waits and in MultipleInners the code continues to create the next Inner.

Or if there's a completely better way of achieving this?

Upvotes: 2

Views: 388

Answers (1)

user4442671
user4442671

Reputation:

A resource of which you need one for each instance of a class, and who's lifetime is the same as that of the matching instance is best represented as a member variable.

So just make the threads member variables of Inner that are launched at construction, and joined at destruction:

template<Owner>
class Inner
{
    Inner(Owner& owner) 
      : _owner(owner)
      , _recv_thread([this](){readFromQueue();}),
      , _read_thread([this](){receiveLoop();}),
    {
    }

    ~Inner() {
      _recv_thread.join();
      _read_thread.join();
    }

    void receiveLoop() {
      while(...) {
        //etc...
        receiveMessage(msg);
      }
    }

    void receiveMessage(const Message& msg)
    {
        // Ommitted locks etc for simplicty
        _queue.push(msg);
    }

    void readFromQueue()
    {
        // Ommitted loop, locks etc for simplicty
        _owner.receiveMessage(msg);
    }


    Owner& _owner;
    std::queue<Message> _queue;
    
    // Make sure these are the last members, so that _owner and _queue 
    // are constructed already when the threads start
    std::thread _recv_thread;
    std::thread _read_thread;

};

Upvotes: 2

Related Questions