pthread is not starting for class instance

白昼怎懂夜的黑 提交于 2020-05-24 04:04:56

问题


NOTE: C++98

Hi, I'm a little new to c++ and I am writing a databaes program and am attempting to start a timer using the boost::asio package using pthread. The aim of the timer is to start after sql queries have been placed inside a buffer, of which will run an execute function if nothing has been received for a period of time. I have managed to get it to compile, but it doesn't look like the pthread instance is starting.

I have called the pthread inside my getInstance method, and the boost::asio alarm has been set up accordingly. What I will show below is that by calling io_run() directly starts the timer falls into a loop within the alarm.

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

So I don't feel like the pthread is even starting. I tried putting a print statement in there to see if it came out, and nothing appeared in my terminal.

---- EDIT ----

I have made changes as per Sehe's advice, however it still does not look like I am able to call the alarm handler (setAlarm()). I had to slightly modify it to be compatible with the whole program, but essentially it is this (I gave the interval time a value of 5000 to give it enough time for the tests):

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

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

You can also see here that it enters and leaves the IO thread, and creates an instance, plus the debug output for timer.expires_from_now(interval):

 INSTANCE CREATED 

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

回答1:


I'm very ccnfused why anyone who uses Boost or C++11 (or both...) would ever use raw pthread threads (see e.g. C++ boost asynchronous timer to run in parallel with program for a good juxtaposition).

The real problem is likely that you have io_service running out of work (see e.g. https://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/reference/io_service__work.html).

If you have no pending async operations the thread just exits.

Another problem is accuracy issues with

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

It's possible that some handlers take so much time that by the time you schedule your next alarm, the deadline has already expired. It's probably better to use

timer.expires_from_now(interval);

Note this also matches the comment better. The comment suffers from comment already because it says "1 second" but it is actually some defined constant DB_WRITE_TIME

or separate your timer from the other handlers in some other way to guarantee accurate scheduling.

Finally, you had UB due to the absense of any shutdown. The static instance never gets destroyed, but what's worth the non-detached thread never is joined, creating undefined behaviour at shutdown.

This problem is actually almost identical to the one recently discussed here, where I also explains the way work guards work in more detail: asio::io_service is ending immediately with work

Here's a c++11 rewrite with the necessary fix:

Since I now noticed you're that person stuck in c++03 land for some weird reason, a Boost Thread version:

C++03 DEMO/Boost Thread

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

Prints

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


来源:https://stackoverflow.com/questions/61498272/pthread-is-not-starting-for-class-instance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!