diff options
Diffstat (limited to 'examples/thread')
-rw-r--r-- | examples/thread/.cvsignore | 6 | ||||
-rw-r--r-- | examples/thread/Makefile.am | 19 | ||||
-rw-r--r-- | examples/thread/dispatcher.cc | 192 | ||||
-rw-r--r-- | examples/thread/dispatcher2.cc | 254 | ||||
-rw-r--r-- | examples/thread/thread.cc | 114 | ||||
-rw-r--r-- | examples/thread/threadpool.cc | 47 |
6 files changed, 632 insertions, 0 deletions
diff --git a/examples/thread/.cvsignore b/examples/thread/.cvsignore new file mode 100644 index 00000000..09983072 --- /dev/null +++ b/examples/thread/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +dispatcher +dispatcher2 +thread +threadpool diff --git a/examples/thread/Makefile.am b/examples/thread/Makefile.am new file mode 100644 index 00000000..43605570 --- /dev/null +++ b/examples/thread/Makefile.am @@ -0,0 +1,19 @@ +include $(top_srcdir)/examples/Makefile.am_fragment + +INCLUDES += $(GTHREAD_CFLAGS) + +## Build the executable, but don't install it. +noinst_PROGRAMS = dispatcher dispatcher2 thread threadpool + +dispatcher_SOURCES = dispatcher.cc +dispatcher_LDFLAGS = $(GTHREAD_LIBS) + +dispatcher2_SOURCES = dispatcher2.cc +dispatcher2_LDFLAGS = $(GTHREAD_LIBS) + +thread_SOURCES = thread.cc +thread_LDFLAGS = $(GTHREAD_LIBS) + +threadpool_SOURCES = threadpool.cc +threadpool_LDFLAGS = $(GTHREAD_LIBS) + diff --git a/examples/thread/dispatcher.cc b/examples/thread/dispatcher.cc new file mode 100644 index 00000000..9cf22dd5 --- /dev/null +++ b/examples/thread/dispatcher.cc @@ -0,0 +1,192 @@ +/* + * Glib::Dispatcher example -- cross thread signalling + * by Daniel Elstner <daniel.elstner@gmx.net> + * + * Copyright (c) 2002 Free Software Foundation + */ + +#include <sigc++/class_slot.h> +#include <glibmm.h> +#include <gtkmm/box.h> +#include <gtkmm/button.h> +#include <gtkmm/buttonbox.h> +#include <gtkmm/main.h> +#include <gtkmm/progressbar.h> +#include <gtkmm/stock.h> +#include <gtkmm/window.h> + +#include <algorithm> +#include <functional> +#include <list> + + +namespace +{ + +class ThreadProgress : public Gtk::ProgressBar +{ +public: + ThreadProgress(); + virtual ~ThreadProgress(); + + void launch(); + SigC::Signal0<void>& signal_finished(); + +private: + unsigned int progress_; + Glib::Dispatcher signal_increment_; + SigC::Signal0<void> signal_finished_; + + void progress_increment(); + void thread_function(); +}; + +class MainWindow : public Gtk::Window +{ +public: + MainWindow(); + virtual ~MainWindow(); + + void launch_threads(); + +protected: + virtual bool on_delete_event(GdkEventAny* event); + +private: + std::list<ThreadProgress*> progress_bars_; + Gtk::Button* close_button_; + + void on_progress_finished(ThreadProgress* progress); +}; + + +ThreadProgress::ThreadProgress() +: + progress_ (0) +{ + // Connect to the cross-thread signal. + signal_increment_.connect(SigC::slot(*this, &ThreadProgress::progress_increment)); +} + +ThreadProgress::~ThreadProgress() +{} + +void ThreadProgress::launch() +{ + // Create a non-joinable thread -- it's deleted automatically on thread exit. + Glib::Thread::create(SigC::slot_class(*this, &ThreadProgress::thread_function), false); +} + +SigC::Signal0<void>& ThreadProgress::signal_finished() +{ + return signal_finished_; +} + +void ThreadProgress::progress_increment() +{ + // Use an integer because floating point arithmetic is inaccurate -- + // we want to finish *exactly* after the 1000th increment. + ++progress_; + + const double fraction = double(progress_) / 1000.0; + set_fraction(std::min(fraction, 1.0)); + + if(progress_ >= 1000) + signal_finished_(); +} + +void ThreadProgress::thread_function() +{ + Glib::Rand rand; + int usecs = 5000; + + for(int i = 0; i < 1000; ++i) + { + usecs = rand.get_int_range(std::max(0, usecs - 1000 - i), std::min(20000, usecs + 1000 + i)); + Glib::usleep(usecs); + + // Tell the GUI thread to increment the progress bar value. + signal_increment_(); + } +} + + +MainWindow::MainWindow() +: + close_button_ (0) +{ + set_title("Thread Dispatcher Example"); + + Gtk::VBox *const vbox = new Gtk::VBox(false, 10); + add(*Gtk::manage(vbox)); + vbox->set_border_width(10); + + for(int i = 0; i < 5; ++i) + { + ThreadProgress *const progress = new ThreadProgress(); + vbox->pack_start(*Gtk::manage(progress), Gtk::PACK_SHRINK); + progress_bars_.push_back(progress); + + progress->signal_finished().connect( + SigC::bind(SigC::slot(*this, &MainWindow::on_progress_finished), progress)); + } + + Gtk::ButtonBox *const button_box = new Gtk::HButtonBox(); + vbox->pack_end(*Gtk::manage(button_box), Gtk::PACK_SHRINK); + + close_button_ = new Gtk::Button(Gtk::Stock::CLOSE); + button_box->pack_start(*Gtk::manage(close_button_), Gtk::PACK_SHRINK); + close_button_->set_flags(Gtk::CAN_DEFAULT); + close_button_->grab_default(); + close_button_->set_sensitive(false); + close_button_->signal_clicked().connect(SigC::slot(*this, &Gtk::Widget::hide)); + + show_all_children(); + set_default_size(300, -1); +} + +MainWindow::~MainWindow() +{} + +void MainWindow::launch_threads() +{ + std::for_each( + progress_bars_.begin(), progress_bars_.end(), + std::mem_fun(&ThreadProgress::launch)); +} + +bool MainWindow::on_delete_event(GdkEventAny*) +{ + // Don't allow closing the window before all threads finished. + return !progress_bars_.empty(); +} + +void MainWindow::on_progress_finished(ThreadProgress* progress) +{ + progress_bars_.remove(progress); + + // Enable the close button when all threads finished. + if(progress_bars_.empty()) + close_button_->set_sensitive(true); +} + +} // anonymous namespace + + +int main(int argc, char** argv) +{ + Glib::thread_init(); + Gtk::Main main_instance (&argc, &argv); + + MainWindow window; + + // Install a one-shot idle handler to launch the threads + // right after the main window has been displayed. + Glib::signal_idle().connect( + SigC::bind_return(SigC::slot(window, &MainWindow::launch_threads), false)); + + Gtk::Main::run(window); + + return 0; +} + diff --git a/examples/thread/dispatcher2.cc b/examples/thread/dispatcher2.cc new file mode 100644 index 00000000..2bb72b90 --- /dev/null +++ b/examples/thread/dispatcher2.cc @@ -0,0 +1,254 @@ +/* + * original Glib::Dispatcher example -- cross thread signalling + * by Daniel Elstner <daniel.elstner@gmx.net> + * + * Modified by Stephan Puchegger <stephan.puchegger@ap.univie.ac.at> + * to contain 2 mainloops in 2 different threads, that communicate + * via cross thread signalling in both directions. The timer thread + * sends the UI thread a cross thread signal every second, which in turn + * updates the label stating how many seconds have passed since the start + * of the program. + * + * Note: This example is special stuff that's seldomly needed by the + * vast majority of applications. Don't bother working out what this + * code does unless you know for sure you need 2 main loops running in + * 2 distinct main contexts. + * + * Copyright (c) 2002 Free Software Foundation + */ + +#include <sigc++/class_slot.h> +#include <glibmm.h> +#include <gtkmm/box.h> +#include <gtkmm/button.h> +#include <gtkmm/buttonbox.h> +#include <gtkmm/main.h> +#include <gtkmm/label.h> +#include <gtkmm/stock.h> +#include <gtkmm/window.h> +#include <sstream> + + +namespace +{ + +class ThreadTimer : public Gtk::Label +{ +public: + ThreadTimer(); + ~ThreadTimer(); + + Glib::Thread* launch(); + void signal_finished_emit(); + +private: + unsigned int time_; + Glib::Dispatcher signal_increment_; + Glib::Dispatcher* signal_finished_ptr_; + + Glib::Mutex startup_mutex_; + Glib::Cond startup_cond_; + + void timer_increment(); + bool timeout_handler(); + static void finished_handler(Glib::RefPtr<Glib::MainLoop> mainloop); + void thread_function(); +}; + +class MainWindow : public Gtk::Window +{ +public: + MainWindow(); + + void launch_thread(); + +protected: + bool on_delete_event(GdkEventAny* event); + void on_button_clicked(); + +private: + Glib::Thread* producer_; + + ThreadTimer* timer_label_; + Gtk::Button* close_button_; +}; + + +ThreadTimer::ThreadTimer() +: + time_ (0), + // Create a new dispatcher that is attached to the default main context, + // which is the one Gtk::Main is using. + signal_increment_ (), + // This pointer will be initialized later by the 2nd thread. + signal_finished_ptr_ (0) +{ + // Connect the cross-thread signal. + signal_increment_.connect(SigC::slot(*this, &ThreadTimer::timer_increment)); +} + +ThreadTimer::~ThreadTimer() +{} + +Glib::Thread* ThreadTimer::launch() +{ + // Unfortunately, the thread creation has to be fully synchronized in + // order to access the Dispatcher object instantiated by the 2nd thread. + // So, let's do some kind of hand-shake using a mutex and a condition + // variable. + Glib::Mutex::Lock lock (startup_mutex_); + + // Create a joinable thread -- it needs to be joined, otherwise it's a memory leak. + Glib::Thread *const thread = Glib::Thread::create( + SigC::slot_class(*this, &ThreadTimer::thread_function), true); + + // Wait for the 2nd thread's startup notification. + while(signal_finished_ptr_ == 0) + startup_cond_.wait(startup_mutex_); + + return thread; +} + +void ThreadTimer::signal_finished_emit() +{ + // Cause the 2nd thread's main loop to quit. + signal_finished_ptr_->emit(); + signal_finished_ptr_ = 0; +} + +void ThreadTimer::timer_increment() +{ + // another second has passed since the start of the program + ++time_; + + std::ostringstream out; + out << time_ << " seconds since start"; + + set_text(out.str()); +} + +// static +void ThreadTimer::finished_handler(Glib::RefPtr<Glib::MainLoop> mainloop) +{ + // quit the timer thread mainloop + mainloop->quit(); +} + +bool ThreadTimer::timeout_handler() +{ + // inform the UI thread that another second has passed + signal_increment_(); + + // this timer should stay alive + return true; +} + +void ThreadTimer::thread_function() +{ + // create a new Main Context + Glib::RefPtr<Glib::MainContext> context = Glib::MainContext::create(); + // create a new Main Loop + Glib::RefPtr<Glib::MainLoop> mainloop = Glib::MainLoop::create(context, true); + + // attach a timeout handler, that is called every second, to the + // newly created MainContext + context->signal_timeout().connect(SigC::slot(*this, &ThreadTimer::timeout_handler), 1000); + + // We need to lock while creating the Dispatcher instance, + // in order to ensure memory visibility. + Glib::Mutex::Lock lock (startup_mutex_); + + // create a new dispatcher, that is connected to the newly + // created MainContext + Glib::Dispatcher signal_finished (context); + signal_finished.connect(SigC::bind(SigC::slot(&ThreadTimer::finished_handler), mainloop)); + + signal_finished_ptr_ = &signal_finished; + + // Tell the launcher thread that everything is in place now. + startup_cond_.signal(); + lock.release(); + + // start the mainloop + mainloop->run(); +} + + +MainWindow::MainWindow() +: + producer_ (0), + timer_label_ (0), + close_button_ (0) +{ + set_title("Thread Dispatcher Example #2"); + + Gtk::VBox *const vbox = new Gtk::VBox(false, 10); + add(*Gtk::manage(vbox)); + vbox->set_border_width(10); + + timer_label_ = new ThreadTimer(); + vbox->pack_start(*Gtk::manage(timer_label_), Gtk::PACK_SHRINK); + timer_label_->set_text("0 seconds since start"); + + Gtk::ButtonBox *const button_box = new Gtk::HButtonBox(); + vbox->pack_end(*Gtk::manage(button_box), Gtk::PACK_SHRINK); + + close_button_ = new Gtk::Button(Gtk::Stock::CLOSE); + button_box->pack_start(*Gtk::manage(close_button_), Gtk::PACK_SHRINK); + close_button_->set_flags(Gtk::CAN_DEFAULT); + close_button_->grab_default(); + close_button_->signal_clicked().connect(SigC::slot(*this, &MainWindow::on_button_clicked)); + + show_all_children(); + set_default_size(300, -1); +} + +void MainWindow::launch_thread() +{ + // launch the timer thread + producer_ = timer_label_->launch(); +} + +bool MainWindow::on_delete_event(GdkEventAny*) +{ + // signal the timer thread to stop + timer_label_->signal_finished_emit(); + + // wait for the timer thread to join + producer_->join(); + + return false; +} + +void MainWindow::on_button_clicked() +{ + // signal the timer thread to stop + timer_label_->signal_finished_emit(); + + // wait for the timer thread to join + producer_->join(); + + // hide toplevel, thus stop the UI thread main loop + hide(); +} + +} // anonymous namespace + + +int main(int argc, char** argv) +{ + Glib::thread_init(); + Gtk::Main main_instance (&argc, &argv); + + MainWindow window; + + // Install a one-shot idle handler to launch the threads + // right after the main window has been displayed. + Glib::signal_idle().connect( + SigC::bind_return(SigC::slot(window, &MainWindow::launch_thread), false)); + + Gtk::Main::run(window); + + return 0; +} + diff --git a/examples/thread/thread.cc b/examples/thread/thread.cc new file mode 100644 index 00000000..aaddff92 --- /dev/null +++ b/examples/thread/thread.cc @@ -0,0 +1,114 @@ + +#include <iostream> +#include <queue> +#include <sigc++/class_slot.h> +#include <glibmm/random.h> +#include <glibmm/thread.h> +#include <glibmm/timer.h> + + +namespace +{ + +class MessageQueue +{ +public: + MessageQueue(); + ~MessageQueue(); + + void producer(); + void consumer(); + +private: + Glib::Mutex mutex_; + Glib::Cond cond_push_; + Glib::Cond cond_pop_; + std::queue<int> queue_; +}; + + +MessageQueue::MessageQueue() +{} + +MessageQueue::~MessageQueue() +{} + +void MessageQueue::producer() +{ + Glib::Rand rand (1234); + + for(int i = 0; i < 200; ++i) + { + { + Glib::Mutex::Lock lock (mutex_); + + while(queue_.size() >= 64) + cond_pop_.wait(mutex_); + + queue_.push(i); + std::cout << '*'; + std::cout.flush(); + + cond_push_.signal(); + } + + if(rand.get_bool()) + continue; + + Glib::usleep(rand.get_int_range(0, 100000)); + } +} + +void MessageQueue::consumer() +{ + Glib::Rand rand (4567); + + for(;;) + { + { + Glib::Mutex::Lock lock (mutex_); + + while(queue_.empty()) + cond_push_.wait(mutex_); + + const int i = queue_.front(); + queue_.pop(); + std::cout << "\x08 \x08"; + std::cout.flush(); + + cond_pop_.signal(); + + if(i >= 199) + break; + } + + if(rand.get_bool()) + continue; + + Glib::usleep(rand.get_int_range(10000, 200000)); + } +} + +} + + +int main(int, char**) +{ + Glib::thread_init(); + + MessageQueue queue; + + Glib::Thread *const producer = Glib::Thread::create( + SigC::slot_class(queue, &MessageQueue::producer), true); + + Glib::Thread *const consumer = Glib::Thread::create( + SigC::slot_class(queue, &MessageQueue::consumer), true); + + producer->join(); + consumer->join(); + + std::cout << std::endl; + + return 0; +} + diff --git a/examples/thread/threadpool.cc b/examples/thread/threadpool.cc new file mode 100644 index 00000000..721f7f2f --- /dev/null +++ b/examples/thread/threadpool.cc @@ -0,0 +1,47 @@ + +#include <iostream> +#include <glibmm/random.h> +#include <glibmm/thread.h> +#include <glibmm/threadpool.h> +#include <glibmm/timer.h> + + +namespace +{ + +Glib::StaticMutex mutex = GLIBMM_STATIC_MUTEX_INIT; + +void print_char(char c) +{ + Glib::Rand rand; + + for(int i = 0; i < 100; ++i) + { + { + Glib::Mutex::Lock lock (mutex); + std::cout << c; + std::cout.flush(); + } + Glib::usleep(rand.get_int_range(10000, 100000)); + } +} + +} // anonymous namespace + + +int main(int, char**) +{ + Glib::thread_init(); + + Glib::ThreadPool pool (10); + + for(char c = 'a'; c <= 'z'; ++c) + pool.push(SigC::bind(SigC::slot(&print_char), c)); + + pool.shutdown(); + + std::cout << std::endl; + + return 0; +} + |