问题
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