[gtkmm-documentation] Add a multi-threaded example program.
- From: Kjell Ahlstedt <kjellahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtkmm-documentation] Add a multi-threaded example program.
- Date: Wed, 7 Aug 2013 08:02:12 +0000 (UTC)
commit 98c676fbeff0f0d5a339acac28cebebccdadcd35
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date: Wed Aug 7 09:55:05 2013 +0200
Add a multi-threaded example program.
* docs/tutorial/C/gtkmm-tutorial-in.xml: Add an Example section in the
Multi-Threaded Programs chapter.
* docs/tutorial/C/figures/multithread.png: New file.
* docs/tutorial/Makefile.am: Add multithread.png.
* examples/Makefile.am: Add the book/multithread example program.
* examples/book/multithread/examplewindow.[h|cc]:
* examples/book/multithread/exampleworker.[h|cc]:
* examples/book/multithread/main.cc: New files.
docs/tutorial/C/figures/multithread.png | Bin 0 -> 18549 bytes
docs/tutorial/C/gtkmm-tutorial-in.xml | 29 +++++
docs/tutorial/Makefile.am | 1 +
examples/Makefile.am | 8 ++
examples/book/multithread/examplewindow.cc | 171 ++++++++++++++++++++++++++++
examples/book/multithread/examplewindow.h | 57 +++++++++
examples/book/multithread/exampleworker.cc | 99 ++++++++++++++++
examples/book/multithread/exampleworker.h | 46 ++++++++
examples/book/multithread/main.cc | 27 +++++
9 files changed, 438 insertions(+), 0 deletions(-)
---
diff --git a/docs/tutorial/C/figures/multithread.png b/docs/tutorial/C/figures/multithread.png
new file mode 100644
index 0000000..3825d22
Binary files /dev/null and b/docs/tutorial/C/figures/multithread.png differ
diff --git a/docs/tutorial/C/gtkmm-tutorial-in.xml b/docs/tutorial/C/gtkmm-tutorial-in.xml
index 8c8927d..d06af01 100644
--- a/docs/tutorial/C/gtkmm-tutorial-in.xml
+++ b/docs/tutorial/C/gtkmm-tutorial-in.xml
@@ -7469,6 +7469,35 @@ object could of course be used instead.
</sect1>
+<sect1 id="sec-multithread-example">
+<title>Example</title>
+<para>
+This is an example program with two threads, one GUI thread, like in all
+>kmm; programs, and one worker thread. The worker thread is created when you
+press the <literal>Start work</literal> button. It is deleted when the work is
+finished, when you press the <literal>Stop work</literal> button, or when you
+press the <literal>Quit</literal> button.
+</para>
+
+<para>
+A <classname>Glib::Dispatcher</classname> is used for sending notifications
+from the worker thread to the GUI thread. The <classname>ExampleWorker</classname>
+class contains data which is accessed by both threads. This data is protected
+by a <classname>Glib::Threads::Mutex</classname>.
+Only the GUI thread updates the GUI.
+</para>
+
+<figure id="figure-multithread">
+ <title>Multi-Threaded Program</title>
+ <screenshot>
+ <graphic format="PNG" fileref="&url_figures_base;multithread.png"/>
+ </screenshot>
+</figure>
+
+<para><ulink url="&url_examples_base;multithread">Source Code</ulink></para>
+
+</sect1>
+
</chapter>
<chapter id="chapter-recommended-techniques">
diff --git a/docs/tutorial/Makefile.am b/docs/tutorial/Makefile.am
index 972d0e5..eac11ae 100644
--- a/docs/tutorial/Makefile.am
+++ b/docs/tutorial/Makefile.am
@@ -75,6 +75,7 @@ DOC_FIGURES = \
figures/menus_and_toolbars.png \
figures/menus_menu.png \
figures/menus_menubar.png \
+ figures/multithread.png \
figures/notebook.png \
figures/paned.png \
figures/printing.png \
diff --git a/examples/Makefile.am b/examples/Makefile.am
index d82731a..a1ad851 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -74,6 +74,7 @@ check_PROGRAMS = \
book/menus/main_menu/main_menu \
book/menus/popup/popup \
book/menus_and_toolbars/example \
+ book/multithread/example \
book/notebook/example \
book/paned/example \
book/printing/advanced/example \
@@ -476,6 +477,13 @@ book_menus_and_toolbars_example_SOURCES = \
book/menus_and_toolbars/examplewindow.h \
book/menus_and_toolbars/main.cc
+book_multithread_example_SOURCES = \
+ book/multithread/examplewindow.cc\
+ book/multithread/examplewindow.h \
+ book/multithread/exampleworker.cc\
+ book/multithread/exampleworker.h \
+ book/multithread/main.cc
+
book_notebook_example_SOURCES = \
book/notebook/examplewindow.cc \
book/notebook/examplewindow.h \
diff --git a/examples/book/multithread/examplewindow.cc b/examples/book/multithread/examplewindow.cc
new file mode 100644
index 0000000..fe50dc4
--- /dev/null
+++ b/examples/book/multithread/examplewindow.cc
@@ -0,0 +1,171 @@
+/* gtkmm example Copyright (C) 2013 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "examplewindow.h"
+#include <iostream>
+
+ExampleWindow::ExampleWindow() :
+ m_VBox(Gtk::ORIENTATION_VERTICAL, 5),
+ m_ButtonBox(Gtk::ORIENTATION_HORIZONTAL),
+ m_ButtonStart("Start work"),
+ m_ButtonStop("Stop work"),
+ m_ButtonQuit("_Quit", /* mnemonic= */ true),
+ m_ProgressBar(),
+ m_ScrolledWindow(),
+ m_TextView(),
+ m_Dispatcher(),
+ m_Worker(),
+ m_WorkerThread(0)
+{
+ set_title("Multi-threaded example");
+ set_border_width(5);
+ set_default_size(300, 300);
+
+ add(m_VBox);
+
+ // Add the ProgressBar.
+ m_VBox.pack_start(m_ProgressBar, Gtk::PACK_SHRINK);
+
+ m_ProgressBar.set_text("Fraction done");
+ m_ProgressBar.set_show_text();
+
+ // Add the TextView, inside a ScrolledWindow.
+ m_ScrolledWindow.add(m_TextView);
+
+ // Only show the scrollbars when they are necessary.
+ m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+
+ m_VBox.pack_start(m_ScrolledWindow);
+
+ m_TextView.set_editable(false);
+
+ // Add the buttons to the ButtonBox.
+ m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK);
+
+ m_ButtonBox.pack_start(m_ButtonStart, Gtk::PACK_SHRINK);
+ m_ButtonBox.pack_start(m_ButtonStop, Gtk::PACK_SHRINK);
+ m_ButtonBox.pack_start(m_ButtonQuit, Gtk::PACK_SHRINK);
+ m_ButtonBox.set_border_width(5);
+ m_ButtonBox.set_spacing(5);
+ m_ButtonBox.set_layout(Gtk::BUTTONBOX_END);
+
+ // Connect the signal handlers to the buttons.
+ m_ButtonStart.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_start_button_clicked));
+ m_ButtonStop.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_stop_button_clicked));
+ m_ButtonQuit.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_quit_button_clicked));
+
+ // Connect the handler to the dispatcher.
+ m_Dispatcher.connect(sigc::mem_fun(*this, &ExampleWindow::on_notification_from_worker_thread));
+
+ // Create a text buffer mark for use in update_widgets().
+ Glib::RefPtr<Gtk::TextBuffer> buffer = m_TextView.get_buffer();
+ buffer->create_mark("last_line", buffer->end(), /* left_gravity= */ true);
+
+ update_start_stop_buttons();
+
+ show_all_children();
+}
+
+void ExampleWindow::on_start_button_clicked()
+{
+ if (m_WorkerThread)
+ {
+ std::cout << "Can't start a worker thread while another one is running." << std::endl;
+ }
+ else
+ {
+ // Start a new worker thread.
+ m_WorkerThread = Glib::Threads::Thread::create(
+ sigc::bind(sigc::mem_fun(m_Worker, &ExampleWorker::do_work), this));
+ }
+ update_start_stop_buttons();
+}
+
+void ExampleWindow::on_stop_button_clicked()
+{
+ if (!m_WorkerThread)
+ {
+ std::cout << "Can't stop a worker thread. None is running." << std::endl;
+ }
+ else
+ {
+ // Order the worker thread to stop.
+ m_Worker.stop_work();
+ m_ButtonStop.set_sensitive(false);
+ }
+}
+
+void ExampleWindow::update_start_stop_buttons()
+{
+ const bool thread_is_running = m_WorkerThread != 0;
+
+ m_ButtonStart.set_sensitive(!thread_is_running);
+ m_ButtonStop.set_sensitive(thread_is_running);
+}
+
+void ExampleWindow::update_widgets()
+{
+ double fraction_done;
+ Glib::ustring message_from_worker_thread;
+ m_Worker.get_data(&fraction_done, &message_from_worker_thread);
+
+ m_ProgressBar.set_fraction(fraction_done);
+
+ if (message_from_worker_thread != m_TextView.get_buffer()->get_text())
+ {
+ Glib::RefPtr<Gtk::TextBuffer> buffer = m_TextView.get_buffer();
+ buffer->set_text(message_from_worker_thread);
+
+ // Scroll the last inserted line into view. That's somewhat complicated.
+ Gtk::TextIter iter = buffer->end();
+ iter.set_line_offset(0); // Beginning of last line
+ Glib::RefPtr<Gtk::TextMark> mark = buffer->get_mark("last_line");
+ buffer->move_mark(mark, iter);
+ m_TextView.scroll_to(mark);
+ // TextView::scroll_to(iter) is not perfect.
+ // We do need a TextMark to always get the last line into view.
+ }
+}
+
+void ExampleWindow::on_quit_button_clicked()
+{
+ if (m_WorkerThread)
+ {
+ // Order the worker thread to stop and wait for it to stop.
+ m_Worker.stop_work();
+ m_WorkerThread->join();
+ }
+ hide();
+}
+
+// notify() is called from ExampleWorker::do_work(). It is executed in the worker
+// thread. It triggers a call to on_notification_from_worker_thread(), which is
+// executed in the GUI thread.
+void ExampleWindow::notify()
+{
+ m_Dispatcher.emit();
+}
+
+void ExampleWindow::on_notification_from_worker_thread()
+{
+ if (m_WorkerThread && m_Worker.has_stopped())
+ {
+ // Work is done.
+ m_WorkerThread->join();
+ m_WorkerThread = 0;
+ update_start_stop_buttons();
+ }
+ update_widgets();
+}
diff --git a/examples/book/multithread/examplewindow.h b/examples/book/multithread/examplewindow.h
new file mode 100644
index 0000000..520be0a
--- /dev/null
+++ b/examples/book/multithread/examplewindow.h
@@ -0,0 +1,57 @@
+/* gtkmm example Copyright (C) 2013 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTKMM_EXAMPLEWINDOW_H
+#define GTKMM_EXAMPLEWINDOW_H
+
+#include <gtkmm.h>
+#include "exampleworker.h"
+
+class ExampleWindow : public Gtk::Window
+{
+public:
+ ExampleWindow();
+
+ // Called from the worker thread.
+ void notify();
+
+private:
+ // Signal handlers.
+ void on_start_button_clicked();
+ void on_stop_button_clicked();
+ void on_quit_button_clicked();
+
+ void update_start_stop_buttons();
+ void update_widgets();
+
+ // Dispatcher handler.
+ void on_notification_from_worker_thread();
+
+ // Member data.
+ Gtk::Box m_VBox;
+ Gtk::ButtonBox m_ButtonBox;
+ Gtk::Button m_ButtonStart;
+ Gtk::Button m_ButtonStop;
+ Gtk::Button m_ButtonQuit;
+ Gtk::ProgressBar m_ProgressBar;
+ Gtk::ScrolledWindow m_ScrolledWindow;
+ Gtk::TextView m_TextView;
+
+ Glib::Dispatcher m_Dispatcher;
+ ExampleWorker m_Worker;
+ Glib::Threads::Thread* m_WorkerThread;
+};
+
+#endif // GTKMM_EXAMPLEWINDOW_H
diff --git a/examples/book/multithread/exampleworker.cc b/examples/book/multithread/exampleworker.cc
new file mode 100644
index 0000000..e5cf2f7
--- /dev/null
+++ b/examples/book/multithread/exampleworker.cc
@@ -0,0 +1,99 @@
+/* gtkmm example Copyright (C) 2013 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "exampleworker.h"
+#include "examplewindow.h"
+#include <sstream>
+
+ExampleWorker::ExampleWorker() :
+ m_Mutex(),
+ m_shall_stop(false),
+ m_has_stopped(false),
+ m_fraction_done(0.0),
+ m_message()
+{
+}
+
+// Accesses to these data are synchronized by a mutex.
+// Some microseconds can be saved by getting all data at once, instead of having
+// separate get_fraction_done() and get_message() methods.
+void ExampleWorker::get_data(double* fraction_done, Glib::ustring* message) const
+{
+ Glib::Threads::Mutex::Lock lock(m_Mutex);
+
+ if (fraction_done)
+ *fraction_done = m_fraction_done;
+
+ if (message)
+ *message = m_message;
+}
+
+void ExampleWorker::stop_work()
+{
+ Glib::Threads::Mutex::Lock lock(m_Mutex);
+ m_shall_stop = true;
+}
+
+bool ExampleWorker::has_stopped() const
+{
+ Glib::Threads::Mutex::Lock lock(m_Mutex);
+ return m_has_stopped;
+}
+
+void ExampleWorker::do_work(ExampleWindow* caller)
+{
+ {
+ Glib::Threads::Mutex::Lock lock(m_Mutex);
+ m_has_stopped = false;
+ m_fraction_done = 0.0;
+ m_message = "";
+ } // The mutex is unlocked here by lock's destructor.
+
+ // Simulate a long calculation.
+ for (int i = 0; ; ++i) // do until break
+ {
+ Glib::usleep(250000); // microseconds
+
+ Glib::Threads::Mutex::Lock lock(m_Mutex);
+
+ m_fraction_done += 0.01;
+
+ if (i % 4 == 3)
+ {
+ std::ostringstream ostr;
+ ostr << (m_fraction_done * 100.0) << "% done\n";
+ m_message += ostr.str();
+ }
+
+ if (m_fraction_done >= 1.0)
+ {
+ m_message += "Finished";
+ break;
+ }
+ if (m_shall_stop)
+ {
+ m_message += "Stopped";
+ break;
+ }
+ lock.release();
+ caller->notify();
+ }
+
+ Glib::Threads::Mutex::Lock lock(m_Mutex);
+ m_shall_stop = false;
+ m_has_stopped = true;
+ lock.release();
+ caller->notify();
+}
diff --git a/examples/book/multithread/exampleworker.h b/examples/book/multithread/exampleworker.h
new file mode 100644
index 0000000..27ac005
--- /dev/null
+++ b/examples/book/multithread/exampleworker.h
@@ -0,0 +1,46 @@
+/* gtkmm example Copyright (C) 2013 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTKMM_EXAMPLEWORKER_H
+#define GTKMM_EXAMPLEWORKER_H
+
+#include <gtkmm.h>
+
+class ExampleWindow;
+
+class ExampleWorker
+{
+public:
+ ExampleWorker();
+
+ // Thread function.
+ void do_work(ExampleWindow* caller);
+
+ void get_data(double* fraction_done, Glib::ustring* message) const;
+ void stop_work();
+ bool has_stopped() const;
+
+private:
+ // Synchronizes access to member data.
+ mutable Glib::Threads::Mutex m_Mutex;
+
+ // Data used by both GUI thread and worker thread.
+ bool m_shall_stop;
+ bool m_has_stopped;
+ double m_fraction_done;
+ Glib::ustring m_message;
+};
+
+#endif // GTKMM_EXAMPLEWORKER_H
diff --git a/examples/book/multithread/main.cc b/examples/book/multithread/main.cc
new file mode 100644
index 0000000..3d7a9e6
--- /dev/null
+++ b/examples/book/multithread/main.cc
@@ -0,0 +1,27 @@
+/* gtkmm example Copyright (C) 2013 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "examplewindow.h"
+#include <gtkmm/application.h>
+
+int main (int argc, char* argv[])
+{
+ Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
+
+ ExampleWindow window;
+
+ //Shows the window and returns when it is closed.
+ return app->run(window);
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]