【问题标题】:C++ threading model for creating multiple instances of class spawning threads用于创建类生成线程的多个实例的 C++ 线程模型
【发布时间】:2021-10-10 15:20:45
【问题描述】:

摘要:

我正在设计一个类 (Inner),它产生 2 个线程——一个生产者和一个消费者。在一种用法中有一个实例,在另一种情况下有多个实例。

在 Standalone 中,我需要两个线程来继续写入/读取消息。但是,如果有多个实例,我需要代码来生成两个线程但继续创建下一个类(以生成更多线程)。

我的问题是尝试将这两种情况与std::thread::join()std::thread::detach() 的正确用法结合起来。

代码详情:

Inner 类生成一个线程来接收和排队消息,以及第二个线程来读取队列并将消息发送到拥有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;
};

有两种可能的所有者类别,SingleInner

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

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

    std::unique_ptr<Inner> _inner;
};

第二个上下文有多个实例:

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;
};

我不确定如何允许 Inner 生成 2 个线程,让它们继续运行,但在 OneInner 中代码等待,在 MultipleInners 中代码继续创建下一个 Inner

或者如果有更好的方法来实现这一点?

【问题讨论】:

    标签: c++ multithreading architecture pthreads


    【解决方案1】:

    类的每个实例都需要一个资源,并且其生命周期与匹配实例的生命周期相同,最好将其表示为成员变量。

    所以只需让Inner的线程成员变量在构造时启动,并在销毁时加入:

    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;
    
    };
    

    【讨论】:

    • 这行得通。不幸的是,我必须将一个参数传递给 receiveMessage(),它是 Google Protobuf,我认为他们已经使它不可复制等,所以还有一些额外的头痛需要解决。
    • @user997112 是的,抱歉,我认为receiveMessage() 不是传递给_read_thread 的正确方法,您可能想要哪个函数具有该线程的主循环。我会修改答案,
    • 超出了问题的范围,但是:您确定每个Inner 都需要一个专用的接收线程吗?这听起来有点不寻常。无论哪个线程产生消息,都可以直接调用receiveMessage(msg)
    • 另外:Protobuf 不可复制,但它们应该可以轻松移动。
    • 我已将接收循环放在另一个类中。所以我的列表初始化器 lambda 看起来像这样:): _receiveThread([this](const GoogleProtobuf&amp; gpb){_udp.listen(getPortsAddresses(std::move(gpb)))}) 但是,我得到了static assertion failed. std::thread arguments must be invocable after conversion to rvalues
    猜你喜欢
    • 1970-01-01
    • 2014-02-17
    • 2018-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多