title: Signals and threads - good or bad design decision?
body: <p>I have to write a program that performs highly computationally intensive calculations. The program might run for several days.
  The calculation can be separated easily in different threads without the need of shared data.
  I want a GUI or a web service that informs me of the current status.</p>
  
  <p>My current design uses BOOST::signals2 and BOOST::thread.
  It compiles and <em>so far</em> works as expected.
  If a thread finished one iteration and new data is available it calls a signal which is connected to a slot in the GUI class.</p>
  
  <p>My question(s):</p>
  
  <ul>
  <li>Is this combination of signals and threads a wise idea? I another forum somebody advised someone else not to "go down this road".</li>
  <li>Are there potential deadly pitfalls nearby that I failed to see?</li>
  <li>Is my expectation realistic that it will be "easy" to use my GUI class to provide a web interface or a QT, a VTK or a whatever window?</li>
  <li>Is there a more clever alternative (like other boost libs) that I overlooked?</li>
  </ul>
  
  <p>following code compiles with</p>
  
  <pre><code>g++ -Wall -o main -lboost_thread-mt &lt;filename&gt;.cpp
  </code></pre>
  
  <p>code follows:</p>
  
  <pre><code>#include &lt;boost/signals2.hpp&gt;
  #include &lt;boost/thread.hpp&gt;
  #include &lt;boost/bind.hpp&gt;
  
  #include &lt;iostream&gt;
  #include &lt;iterator&gt;
  #include &lt;string&gt;
  
  using std::cout;
  using std::cerr;
  using std::string;
  
  /**
   * Called when a CalcThread finished a new bunch of data.
   */
  boost::signals2::signal&lt;void(string)&gt; signal_new_data;
  
  /**
   * The whole data will be stored here.
   */
  class DataCollector
  {
      typedef boost::mutex::scoped_lock scoped_lock;
      boost::mutex mutex;
  
  public:
      /**
       * Called by CalcThreads call the to store their data.
       */
      void push(const string &amp;s, const string &amp;caller_name)
      {
          scoped_lock lock(mutex);
          _data.push_back(s);
          signal_new_data(caller_name);
      }
  
      /**
       * Output everything collected so far to std::out.
       */
      void out()
      {
          typedef std::vector&lt;string&gt;::const_iterator iter;
          for (iter i = _data.begin(); i != _data.end(); ++i)
              cout &lt;&lt; " " &lt;&lt; *i &lt;&lt; "\n";
      }
  
  private:
      std::vector&lt;string&gt; _data;
  };
  
  /**
   * Several of those can calculate stuff.
   * No data sharing needed.
   */
  struct CalcThread
  {
      CalcThread(string name, DataCollector &amp;datcol) :
          _name(name), _datcol(datcol)
      {
  
      }
  
      /**
       * Expensive algorithms will be implemented here.
       * @param num_results how many data sets are to be calculated by this thread.
       */
      void operator()(int num_results)
      {
          for (int i = 1; i &lt;= num_results; ++i)
          {
              std::stringstream s;
              s &lt;&lt; "[";
              if (i == num_results)
                  s &lt;&lt; "LAST ";
              s &lt;&lt; "DATA " &lt;&lt; i &lt;&lt; " from thread " &lt;&lt; _name &lt;&lt; "]";
              _datcol.push(s.str(), _name);
          }
      }
  
  private:
      string _name;
      DataCollector &amp;_datcol;
  };
  
  /**
   * Maybe some VTK or QT or both will be used someday.
   */
  class GuiClass
  {
  public:
      GuiClass(DataCollector &amp;datcol) :
          _datcol(datcol)
      {
  
      }
  
      /**
       * If the GUI wants to present or at least count the data collected so far.
       * @param caller_name is the name of the thread whose data is new.
       */
      void slot_data_changed(string caller_name) const
      {
          cout &lt;&lt; "GuiClass knows: new data from " &lt;&lt; caller_name &lt;&lt; std::endl;
      }
  
  private:
      DataCollector &amp; _datcol;
  
  };
  
  int main()
  {
      DataCollector datcol;
  
      GuiClass mc(datcol);
      signal_new_data.connect(boost::bind(&amp;GuiClass::slot_data_changed, &amp;mc, _1));
  
      CalcThread r1("A", datcol), r2("B", datcol), r3("C", datcol), r4("D",
              datcol), r5("E", datcol);
  
      boost::thread t1(r1, 3);
      boost::thread t2(r2, 1);
      boost::thread t3(r3, 2);
      boost::thread t4(r4, 2);
      boost::thread t5(r5, 3);
  
      t1.join();
      t2.join();
      t3.join();
      t4.join();
      t5.join();
  
      datcol.out();
  
      cout &lt;&lt; "\nDone" &lt;&lt; std::endl;
      return 0;
  }
  </code></pre>