I
had a similar problem and I show you how I solved it.
The
logic a little bit (not sure if you like it, but I find this way to usually be
more maintainable and scalable) .
Basically
the “main” thread calls “Log” that pushes the string
into a queue. The access to the queue is synchronized by the “queue_mutex”.
There
are 2 additional mutexes:
·
work_mutex makes “Run” wait until
the queue is *not* empty.
·
empty_mutex
makes “WaitForQueueFlush”
wait until the queue *is* empty.
Maybe
you can achieve the same combining those two mutexes but I don’t have
time to try that right now.
Hope
it can help,
Benedetto
This
is an (untested) code snippet.
#include <boost/thread/thread.hpp>
#include <boost/thread/xtime.hpp>
#include <boost/thread/condition.hpp>
#include <boost/foreach.hpp>
#include <boost/tr1/functional.hpp>
#include <queue>
#include <fstream>
#include <string>
class FileLogger
{
typedef boost::unique_lock<boost::mutex>
unique_lock;
std::ofstream logfile;
bool running;
// this controls the actual push/pop in the queue
boost::mutex queue_mutex;
// this mutex will be "unlocked" when the
queue is empty.
// WaitForQueueFlush will wait until it is unlocked
and then lock it.
// It will be unlocked when eventually the queue
becomes empty again.
boost::mutex empty_mutex;
// this mutex will be "locked" when the
queue is empty.
// the "Run" method will wait on it until
some element is pushed into the queue
// and this mutex becomes available.
boost::mutex work_mutex;
// queue seems to be more appropriate data structure
than vector
std::queue<std::string> queue;
// this saves the current thread... just in case you
need it
boost::thread * mythread;
public:
FileLogger(const std::string& filename)
: logfile(filename.c_str())
, running(true)
{
work_mutex.lock();
}
~FileLogger()
{
running = false;
}
// this runs in the “main” thread
void Log(const
std::string& s)
{
unique_lock lock(queue_mutex);
queue.push(s);
if (!work_mutex.try_lock()) {
work_mutex.unlock();
}
if (empty_mutex.try_lock()) {
empty_mutex.unlock();
}
}
void Log_impl(const
std::string& s)
{
unique_lock lock(queue_mutex);
logfile << s << '\n';
}
void WaitForQueueFlush()
{
empty_mutex.lock();
}
void start()
{
mythread = new
boost::thread(boost::bind(&FileLogger::Run, this));
}
private:
void Run()
{
while( running ) {
work_mutex.lock();
unique_lock lock(queue_mutex);
std::string s = queue.pop();
Log_impl(s);
if (queue.empty())
{
empty_mutex.unlock();
}
}
}
};
int main()
{
FileLogger fileLogger("logfile.txt");
fileLogger.Log(std::string(1000*1000, '*'));
// this call should wait, synchronously, until
// current queue has been flushed to disk
fileLogger.WaitForQueueFlush();
}
-----Original Message-----
From: boost-users-bounces@lists.boost.org
[mailto:boost-users-bounces@lists.boost.org] On Behalf Of Daniel Lidström
Sent: Friday, March 05, 2010 7:22 AM
To: boost-users@lists.boost.org
Subject: [Boost-users] Waiting for a thread on another thread
Hello!
I have created a simple file logger using the active
object pattern. This is intended to
offload logging operations to a separate thread, to not
disturb the main thread with time-
consuming file operations. It works very nicely with
boost::thread, but now I have a new
desired functionality. The file logger keeps an internal
queue of strings that need to be
written to file. I want to know, from the main thread,
when this queue is empty. To illustrate
I have created a minimal, hopefully compilable, sample
that shows what I am trying to achieve.
#include <boost/thread/thread.hpp>
#include <boost/thread/xtime.hpp>
#include <boost/thread/condition.hpp>
#include <boost/foreach.hpp>
#include <boost/tr1/functional.hpp>
#include <vector>
#include <fstream>
#include <string>
class FileLogger
{
typedef boost::unique_lock<boost::mutex>
unique_lock;
std::ofstream logfile;
bool running;
boost::thread running_thread;
boost::mutex mutex;
boost::condition_variable condition;
std::vector<std::string> queue;
public:
FileLogger(const std::string& filename)
: logfile(filename.c_str())
, running(true)
,
running_thread(std::tr1::bind(&FileLogger::Run, this))
{ }
~FileLogger()
{
running = false;
condition.notify_one();
running_thread.join();
}
void Log(const std::string& s)
{
unique_lock lock(mutex);
queue.push_back(s);
}
void WaitForQueueFlush()
{
// ??
}
private:
void Run()
{
while( running ) {
std::vector<std::string> copyList;
{
// wait 0.1 s here, for queue to fill up
boost::xtime sleepTime;
boost::xtime_get(&sleepTime,
boost::TIME_UTC);
sleepTime.nsec += 100*1000*1000;
unique_lock lock(mutex);
condition.timed_wait(lock, sleepTime);
// fetch the current message list
copyList.swap(queue);
}
BOOST_FOREACH(const std::string& s,
copyList) {
logfile << s << '\n';
}
logfile.flush();
}
}
};
int main()
{
FileLogger fileLogger("logfile.txt");
fileLogger.Log(std::string(1000*1000, '*'));
// this call should wait, synchronously, until
// current queue has been flushed to disk
fileLogger.WaitForQueueFlush();
}
So the problem is to implement
FileLogger::WaitForQueueFlush. How can I
signal the main thread when the queue has been emptied?
Thanks in advance!
Regards,
Daniel Lidström
Stockholm, Sweden
_______________________________________________
Boost-users mailing list
Boost-users@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users