【问题标题】:pthread is not starting for class instancepthread 没有为类实例启动
【发布时间】:2020-04-29 09:16:56
【问题描述】:

注意:C++98

嗨,我对 c++ 有点陌生,我正在编写一个数据库程序,并尝试使用 pthread 的 boost::asio 包启动一个计时器。计时器的目的是在 sql 查询放入缓冲区后启动,如果一段时间内没有收到任何内容,缓冲区将运行执行函数。我已经设法让它编译,但它看起来不像 pthread 实例正在启动。

我在 getInstance 方法中调用了 pthread,并且相应地设置了 boost::asio 警报。下面我要展示的是,通过调用io_run()直接启动定时器会陷入闹钟内的循环。

database.h

void *run_io(void *arg);

class Database
{
private:
    static Database *dbInstance; //= NULL;

public:
    boost::asio::io_service io_service;
    boost::posix_time::millisec interval;
    boost::asio::deadline_timer timer;
    pthread_t timerThread;

public:
    static Database &getInstance()
    {
        if (!dbInstance)
        {
            dbInstance = new Database();
            // pthread_create(&dbInstance->timerThread,NULL,run_io,&dbInstance->io_service);
            std::cout << " INSTANCE CREATED " << std::endl;
            pthread_create(&dbInstance->timerThread, NULL, run_io, (void *)&dbInstance->io_service);
            // pthread_join(&dbInstance->timerThread, NULL);
        }
        return *dbInstance;
    }
};

database.cpp

Database *Database::dbInstance = NULL;

Database::Database()
    : interval(2000), timer(io_service, interval) {}

Database::~Database()
{
    sqlite3_close(db);
}

void Database::setAlarm(const boost::system::error_code& /* e */)
{
    std::cout << "[TEST] WE ARE IN SET ALARM " << std::endl;
    DB_WRITE_TIME = 500;

    boost::posix_time::milliseconds interval(DB_WRITE_TIME);

    // Reschedule the timer for 1 second in the future:
    timer.expires_at(timer.expires_at() + interval);
    // Posts the timer event
    timer.async_wait(boost::bind(&Database::setAlarm, this, _1));
}

int Database::buffer()
{
    // DO BUFFER STUFF

    timer.async_wait(boost::bind(&Database::setAlarm, this, _1));
   // io_service.run() <-- uncommenting this results in the loop
    return rc ;
}

void *run_io(void *arg)
{
    boost::asio::io_service *io_service = (boost::asio::io_service *)arg;

    io_service->run();
}

所以我觉得 pthread 甚至都没有启动。我试着在里面放一个打印语句,看看它是否出来了,我的终端上什么也没有。

---- 编辑----

我已按照 Sehe 的建议进行了更改,但看起来我仍然无法调用警报处理程序 (setAlarm())。我不得不稍微修改它以与整​​个程序兼容,但本质上是这样的(我给了间隔时间一个 5000 的值,以便给它足够的时间进行测试):

database.h

class Database
{
private:
    static boost::shared_ptr<Database> dbInstance;

private:
    typedef boost::asio::io_service io_service;
    io_service io;
    boost::scoped_ptr<io_service::work> work;
    boost::posix_time::millisec interval;
    boost::asio::deadline_timer timer;
    boost::thread timerThread;

    void run_io()
    {
        std::cout << "ENTER IO THREAD" << std::endl;
        io.run();
        std::cout << "LEAVE IO THREAD" << std::endl;
    }

public:
    static Database &getInstance()
    {
        if (!dbInstance)
        {
            std::cout << " INSTANCE CREATED " << std::endl;
            dbInstance.reset(new Database());
            dbInstance->timerThread = boost::thread(boost::bind(&Database::run_io,dbInstance));
        }
        return *dbInstance;
    }

    Database(); // <-- default constructor (doesn't take any args)
    ~Database();

database.cpp

boost::shared_ptr<Database> Database::dbInstance;
static const int DB_WRITE_TIME = 5000;

Database::Database()
    : work(new io_service::work(io)), interval(5000), timer(io, interval)
{
    // std::cout << " CONSTRUCTED " << std::endl;
}

Database::~Database()
{
    // std::cout << " DESTROYED " << std::endl;
    // sqlite3_close(db);
}

void Database::setAlarm(const boost::system::error_code& ec)
{
    std::cout << "[TEST] WE ARE IN SET ALARM - ec message = " << ec.message() << std::endl;

    executeSqlInBuffer(); // once timer expire, call the execute function

    if(!ec)
    {
        boost::posix_time::milliseconds interval(DB_WRITE_TIME);
        timer.expires_from_now(interval);
        timer.async_wait(boost::bind(&Database::setAlarm, this, _1));
    }
}

void Database::teardown()
{
    // std::cout << " INSTANCE SHUTTING DOWN " << std::endl;
    timer.cancel();             // stop timer loop
    work.reset();               // allows io.run() to exit
    if(timerThread.joinable())
    {
        std::cout << " JOINED " << std::endl;
        timerThread.join();     // releasing bound of shared_ptr
    }
    else std::cout << " NOT JOINED " << std::endl;
    dbInstance.reset();         // releasing instance
}

int Database::buffer()
{
    // do buffering
    if(buffer.size() == max_size)
    {    
        executeSqlInBuffer();
    }
    std::cout << timer.expires_from_now(interval) << std::endl;
    // std::cout << " ~ BEFORE TIMER ~ " << std::endl;
    timer.async_wait(boost::bind(&Database::setAlarm, this, _1));

    return 1;
}

ma​​in.cpp

int main()
{
    pthread_t thread1;        // a few pthreads in main that handle other areas of the program.
    pthread_create(&thread1,NULL,thread1Arg,NULL);

    pthread_t dbThread;        // my pthread for the database
    pthread_create(&dbThread,NULL,dbThreadArg,NULL);

    Database& database = Database::getInstance();
    database.teardown();

    pthread_join(thread1,NULL);
    pthread_join(dbThread,NULL);

    return 0;
}

这里也可以看到它进入和离开IO线程,并创建了一个实例,加上timer.expires_from_now(interval)的调试输出:

 INSTANCE CREATED 

 JOINED 
ENTER IO THREAD
LEAVE IO THREAD
...
...
0 ---> first cycle
1 ---> second cycle
...
1 ---> nth cycle 

【问题讨论】:

    标签: asynchronous io pthreads boost-asio c++98


    【解决方案1】:

    我很困惑为什么使用 Boost 或 C++11(或两者都...)的人会使用原始的 pthread 线程(例如,请参阅 C++ boost asynchronous timer to run in parallel with program 以获得良好的并列)。

    真正的问题可能是您的io_service 用完了工作(参见例如https://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/reference/io_service__work.html)。

    如果您没有挂起的异步操作,则线程将退出。

    另一个问题是准确性问题

    timer.expires_at(timer.expires_at() + interval);
    

    某些处理程序可能会花费大量时间,以至于在您安排下一个警报时,截止日期已经到期。使用起来可能会更好

    timer.expires_from_now(interval);
    

    请注意,这也更好地匹配评论。该评论已经受到评论的影响,因为它说“1秒”但它实际上是一些定义的常量DB_WRITE_TIME

    或以其他方式将您的计时器与其他处理程序分开,以保证准确的调度。

    最后,由于没有任何关机,您拥有UB。静态实例永远不会被破坏,但非分离线程永远不会被加入,从而在关闭时创建未定义的行为。

    这个问题实际上与最近在这里讨论的问题几乎相同,我还更详细地解释了work 守卫的工作方式:asio::io_service is ending immediately with work

    这是一个带有必要修复的 c++11 重写:

    因为我现在注意到你是那个出于某种奇怪的原因被困在 c++03 领域的人,所以是 Boost Thread 版本:

    C++03 DEMO/Boost 线程

    Live On Coliru

    #include <boost/asio.hpp>
    #include <boost/make_shared.hpp>
    #include <boost/scoped_ptr.hpp>
    #include <boost/thread.hpp>
    #include <iostream>
    
    static const int DB_WRITE_TIME = 500;
    
    class Database
    {
      private:
        static boost::shared_ptr<Database> dbInstance;
    
        Database()
            : work(new io_service::work(io)),
              interval(750),
              timer(io, interval)
        {
            std::cout << "INSTANCE CREATED" << std::endl;
        }
    
        void on_timer_completed(const boost::system::error_code& ec) {
            std::cout << "[on_timer_completed] " << ec.message() << std::endl;
    
            if (!ec) {
                boost::posix_time::milliseconds interval(DB_WRITE_TIME);
    
                // Reschedule the timer
                timer.expires_from_now(interval);
                timer.async_wait(boost::bind(&Database::on_timer_completed, this, _1));
            }
        }
    
        int buffer()
        {
            // DO BUFFER STUFF
    
            timer.expires_from_now(interval);
            timer.async_wait(boost::bind(&Database::on_timer_completed, this, _1));
            // io_service.run() <-- uncommenting this results in the loop
            return 1; // rc ;
        }
    
      public:
        void do_stuff() {
            buffer(); // whatever it does
        }
    
        void teardown() {
            std::cout << "INSTANCE SHUTTING DOWN\n";
            timer.cancel(); // stop timer loop
            work.reset();   // allows io.run() to exit
            if (timerThread.joinable()) {
                timerThread.join(); // releasing the bound shared_ptr
            }
            dbInstance.reset(); // releasing the instance
        }
    
        ~Database() {
            //sqlite3_close(db);
            std::cout << "INSTANCE DESTROYED\n";
        }
    
      private:
        typedef boost::asio::io_service io_service;
        io_service io;
        boost::scoped_ptr<io_service::work> work;
        boost::posix_time::millisec interval;
        boost::asio::deadline_timer timer;
        boost::thread timerThread;
    
        void run_io() {
            std::cout << "ENTER IO THREAD" << std::endl;
            io.run();
            std::cout << "LEAVE IO THREAD" << std::endl;
        }
    public:
        static Database &getInstance()
        {
            if (!dbInstance)
            {
                dbInstance.reset(new Database());
                dbInstance->timerThread =
                    boost::thread(boost::bind(&Database::run_io, dbInstance));
            }
            return *dbInstance;
        }
    };
    
    boost::shared_ptr<Database> Database::dbInstance;
    
    int main() {
        Database& db = Database::getInstance();
        boost::this_thread::sleep_for(boost::chrono::seconds(1));
    
        db.do_stuff();
        boost::this_thread::sleep_for(boost::chrono::seconds(3));
        // ....
    
        db.teardown();
    }
    

    打印

    INSTANCE CREATED
    ENTER IO THREAD
    [on_timer_completed] Success
    [on_timer_completed] Success
    [on_timer_completed] Success
    [on_timer_completed] Success
    [on_timer_completed] Success
    INSTANCE SHUTTING DOWN
    [on_timer_completed] Operation canceled
    LEAVE IO THREAD
    INSTANCE DESTROYED
    

    【讨论】:

    • 嘿,Sehe,非常感谢您的解释。我已经进行了您推荐的更改,但是由于某种原因,它仍然看起来不像正在调用警报处理程序。我进行了一些调试打印,发现timer.expires_from_now(interval) 的返回从0 开始,然后循环为整个程序的1,而不是调用警报处理程序。
    猜你喜欢
    • 2017-12-04
    • 2021-08-03
    • 1970-01-01
    • 1970-01-01
    • 2020-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多