简体   繁体   English

在每次读取调用时创建 std::thread/std::jthread 的新实例

[英]Create new instance of std::thread/std::jthread on every read call

I am developing a serial port program using boost::asio.我正在使用 boost::asio 开发一个串口程序。
In synchronous mode I create a thread every time read_sync function is called.在同步模式下,每次调用 read_sync function 时我都会创建一个线程。 All reading related operation are carried in this thread (implementation is in read_sync_impl function).所有读取相关的操作都在这个线程中进行(实现在 read_sync_impl 函数中)。
On close_port or stop_read function reading operation is stopped.close_portstop_read function 读取操作停止。
This stopped reading operation can be restarted by calling the read_sync function again.这个停止的读取操作可以通过再次调用read_sync function 来重新启动。
read_sync function will never be called successively without calling close_port or stop_read function in between.如果不调用close_portread_sync function,将永远不会连续调用 read_sync stop_read

I wish to know how to implement a class wide std::jthread along with proper destructor when I call my read_sync function.当我调用我的 read_sync function 时,我想知道如何实现 class 宽std::jthread以及适当的析构函数。 In languages like Kotlin or Dart the garbage-collector takes care of this.在像 Kotlin 或 Dart 这样的语言中,垃圾收集器会处理这个问题。 What is C++ implementation of this.什么是 C++ 实现。

bool SerialPort::read_sync(std::uint32_t read_length, std::int32_t read_timeout)
{
    this->thread_sync_read = std::jthread(&SerialPort::read_sync_impl, this);
    return true;
}

bool SerialPort::read_sync_impl(const std::stop_token& st)
{
    while(true)
    {
        ...
        if (st.stop_requested())
        {
            PLOG_INFO << "Stop Requested. Exiting thread.";
            break;
        }
    }
}

bool SerialPort::close_port(void)
{
    this->thread_sync_read->request_stop();
    this->thread_sync_read->join();
    this->port.close();
    return this->port.is_open();
}

class SerialPort
{
public :
    std::jthread *thread_sync_read = nullptr;
    ...
}

Actual Code实际代码

bool SerialPort::read_sync(std::uint32_t read_length, std::int32_t read_timeout)
{
    try
    {
        if (read_timeout not_eq ignore_read_timeout)
            this->read_timeout = read_timeout;//If read_timeout is not set to ignore_read_timeout, update the read_timeout else use old read_timeout
        if (this->thread_sync_read.joinable())
            return false; // Thread is already running
        thread_sync_read = std::jthread(&SerialPort::read_sync_impl, this);
        return true;
    }
    catch (const std::exception& ex)
    {
        PLOG_ERROR << ex.what();
        return false;
    }
}

void SerialPort::read_sync_impl(const std::stop_token& st)
{
    try
    {
        while (true)
        {
            if (st.stop_requested())
            {
                PLOG_INFO << "Stop Requested in SerialPort::read_sync_impl. Exiting thread.";
                break;
            }
        }
    }
    catch (const std::exception& ex)
    {
        PLOG_ERROR << ex.what();
    }
}

class SerialPort
{
    std::jthread                    thread_sync_read;
    SerialPort() : io(), port(io), thread_sync_read()
    {
        read_buffer.fill(std::byte(0));
        write_buffer.fill(std::byte(0));
    }
}

You don't need to deal with the jthread 's destructor.您不需要处理jthread的析构函数。 A thread object constructed without constructor arguments (default constructor), or one that has been joined, is in an empty state.没有构造函数 arguments(默认构造函数)的线程 object 或已连接的线程位于空的 state 中。 This can act as a stand-in for your nullptr .这可以充当您的nullptr的替身。

class SerialPort
{
public :
    std::jthread thread_sync_read;
    ...

    SerialPort(...)
    : thread_sync_read() // no explicit constructor call needed, just for show
    {}
    SerialPort(SerialPort&&) = delete; // see side notes below
    SerialPort& operator=(SerialPort&&) = delete;

    ~SerialPort()
    {
        if(thread_sync_read.joinable())
            close_port();
    }
    bool read_sync(std::uint32_t read_length, std::int32_t read_timeout)
    {
        if(thread_sync_read.joinable())
            return false; // already reading
        /* start via lambda to work around parameter resolution
         * issues when using member function pointer
         */
        thread_sync_read = std::jthread(
          [this](const std::stop_token& st) mutable {
            return read_sync_impl(st);
          }
        );
        return true;
    }
    bool close_port()
    {
         thread_sync_read.request_stop();
         thread_sync_read.join(); // after this will be back in empty state
         port.close();
         return port.is_open();
    }
};

Side notes旁注

  • Starting and stopping threads is rather expensive.启动和停止线程相当昂贵。 Normally you would want to keep a single worker thread alive and feed it new read/write requests via a work queue or something like that.通常,您希望保持单个工作线程处于活动状态并通过工作队列或类似的东西为其提供新的读/写请求。 But there is nothing wrong with using a simpler design like yours, especially when starting and stopping are rare operations但是使用像您这样更简单的设计并没有错,尤其是在启动和停止是罕见的操作时
  • In the code above I delete the move constructor and assignment operator.在上面的代码中,我删除了移动构造函数和赋值运算符。 The reason is that the thread captures the this pointer.原因是线程捕获了this指针。 Moving the SerialPort while the thread runs would lead to it accessing a dangling pointer在线程运行时移动SerialPort会导致它访问一个悬空指针

You're already reinitialize (move new one into) thread_sync_read in SerialPort::read_sync , everything should works.您已经在SerialPort::read_sync中重新初始化(移动新的) thread_sync_read ,一切都应该正常。


at destructor, you need to remember delete read_sync在析构函数中,你需要记住 delete read_sync

SerialPort::~SerialPort(){
  close_port(); // if necessary to close port
  delete thread_sync_read;
}

or if you declare thread_sync_read not as (raw) pointer或者如果您声明thread_sync_read不是(原始)指针

class SerialPort{
public:
    std::jthread thread_sync_read;
}

then you don't need to delete it.那么你不需要删除它。

SerialPort::~SerialPort(){
  close_port(); // if necessary
}

note that the destructor of std::jthread would perform necessary request_stop() and join() by itself.请注意, std::jthread的析构函数会自行执行必要的request_stop()join()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM