pthread не запускается для экземпляра класса - PullRequest
1 голос
/ 29 апреля 2020

ПРИМЕЧАНИЕ: C ++ 98

Привет, я немного новичок в c ++, и я пишу программу для баз данных и пытаюсь запустить таймер, используя boost :: Пакет asio с использованием pthread. Целью таймера является запуск после того, как sql запросов были помещены в буфер, из которых будет запущена функция выполнения, если в течение некоторого времени ничего не было получено. Мне удалось заставить его скомпилироваться, но это не похоже на запуск экземпляра pthread.

Я вызвал pthread внутри моего метода getInstance, и соответственно был установлен сигнал boost :: asio , Ниже я покажу, что при непосредственном вызове io_run() таймер попадает в al oop в пределах тревоги.

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

база данных. 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 даже запускается. Я попытался поместить там заявление для печати, чтобы увидеть, появилось ли оно, и в моем терминале ничего не появилось.

---- EDIT ----

Я внес изменения в соответствии с инструкциями Sehe's совет, однако это все еще не похоже, что я в состоянии вызвать обработчик тревоги (setAlarm()). Мне пришлось немного изменить его, чтобы он был совместим со всей программой, но по сути это так (я дал интервалу время 5000, чтобы дать ему достаточно времени для тестов):

база данных. 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();

база данных. 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;
}

Здесь также видно, что он входит и выходит из потока ввода-вывода и создает экземпляр, а также отладочный вывод для timer.expires_from_now(interval):

 INSTANCE CREATED 

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

1 Ответ

1 голос
/ 29 апреля 2020

Я очень удивлен, почему любой, кто использует Boost или C ++ 11 (или оба ...), будет когда-либо использовать необработанные потоки pthread (см., Например, C ++ boost, асинхронный таймер для параллельной работы с программой *). 1003 * для хорошего сопоставления).

Реальная проблема, вероятно, в том, что у вас io_service заканчивается работа (см., Например, https://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/reference/io_service__work.html).

Если у вас нет ожидающих асинхронных c операций, поток просто завершается.

Другая проблема связана с проблемами точности с

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

Возможно, что некоторые обработчики отнимают так много времени что к тому времени, когда вы запланируете свой следующий сигнал тревоги, срок уже истек. Вероятно, лучше использовать

timer.expires_from_now(interval);

Обратите внимание, это также лучше соответствует комментарию. Комментарий уже страдает от комментария, потому что он говорит «1 секунда», но на самом деле это определенная константа DB_WRITE_TIME

или отделение вашего таймера от других обработчиков другим способом, чтобы гарантировать точное планирование.

Наконец, у вас было UB из-за отсутствия какого-либо отключения. Экземпляр stati c никогда не уничтожается, но то, что стоит неотделенному потоку, никогда не присоединяется, создавая неопределенное поведение при завершении работы.

Эта проблема на самом деле почти идентична той, которая недавно обсуждалась здесь, где я также объясняю, как work охранники работают более подробно: asio :: io_service немедленно завершается работой

Вот переписывание c ++ 11 с необходимое исправление:

Так как теперь я заметил, что вы застряли в c ++ 03 по какой-то странной причине, версия Boost Thread:

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

Отпечатки

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
...