0

I'm using Gtkmm and multithreading.

I have a class "NetworkWorker" doig stuffs with the network in a secondary thread. In this class i want to make many signals which will be handled by my class "MainWindow".

The methods which handle these signals, will edit append text in a TextView.

I have the following code:

NetworkWorker.h

#ifndef         NETWORKWORKER_H_
# define        NETWORKWORKER_H_

# include       <sigc++/sigc++.h>
# include       <glibmm/threads.h>
# include       <string>

class NetworkWorker
{
 public:
  NetworkWorker();
  ~NetworkWorker();

  void start();
  void stop();

  sigc::signal<void, std::string&>& signal_data_received();

 private:
  void run();

  sigc::signal<void, std::string&> m_signal_data_received;

  Glib::Threads::Thread* m_thread;
  Glib::Threads::Mutex m_mutex;
  bool m_stop;
};

#endif

NetworkWorker.c

#include        <cstdlib>
#include        <glibmm/timer.h>
#include        <glibmm/threads.h>
#include        <iostream>
#include        <sigc++/sigc++.h>

#include        "NetworkWorker.h"

NetworkWorker::NetworkWorker() :
  m_thread(NULL), m_stop(false)
{

}

NetworkWorker::~NetworkWorker()
{
  stop();
}

void NetworkWorker::start()
{
  if (!m_thread)
    m_thread = Glib::Threads::Thread::create(sigc::mem_fun(*this, &NetworkWorker::run));
}

void NetworkWorker::stop()
{
  {
    Glib::Threads::Mutex::Lock lock(m_mutex);
    m_stop = true;
  }

  if (m_thread)
    m_thread->join();
}

sigc::signal<void, std::string&>& NetworkWorker::signal_data_received()
{
  return m_signal_data_received;
}

void NetworkWorker::run()
{
  while (true)
    {
      {
        Glib::Threads::Mutex::Lock lock(m_mutex);
        if (m_stop)
          break;
      }
      Glib::usleep(5000);
      std::cout << "Thread" << std::endl;
      std::string* str = new std::string("MyData");
      m_signal_data_received.emit(*str);
    }
}

MainWindow.h

#ifndef         MAIN_WINDOW_H_
# define        MAIN_WINDOW_H_

# include       <gtkmm/textview.h>
# include       <gtkmm/window.h>
# include       <string>

class MainWindow : public Gtk::Window
{
 public:
  MainWindow();
  ~MainWindow();

  void appendText(const std::string& str);

 private:
  Gtk::TextView m_text_view;
};

#endif

MainWindow.c

#include        <gtkmm/notebook.h>
#include        <gtkmm/widget.h>
#include        <iostream>
#include        <string>

#include        "MainWindow.h"

MainWindow::MainWindow()
{
  set_title("My App");
  set_default_size(800, 600);
  add(m_text_view);
}

MainWindow::~MainWindow()
{

}

void MainWindow::appendText(const std::string& str)
{
  std::string final_text = str + "\n";
  Glib::RefPtr<Gtk::TextBuffer> buffer = m_text_view.get_buffer();
  Gtk::TextBuffer::iterator it = buffer->end();
  buffer->insert(it, final_text);
  Glib::RefPtr<Gtk::Adjustment> adj = m_text_view.get_vadjustment();
  adj->set_value(adj->get_upper() - adj->get_page_size());
}

and my main.cpp

#include        <cstdlib>
#include        <gtkmm/main.h>
#include        <iostream>
#include        <string>

#include        "MainWindow.h"
#include        "NetworkWorker.h"

void            recv(const std::string& str)
{
  std::cout << str << std::endl;
}

int             main(int argc, char **argv)
{
  Gtk::Main     app(Gtk::Main(argc, argv));
  MainWindow    main_window;
  NetworkWorker network_worker;

  main_window.show_all();

  network_worker.signal_data_received().connect(sigc::ptr_fun(&recv));
  network_worker.signal_data_received().connect(sigc::mem_fun(main_window, &MainWindow::appendText));
  network_worker.start();

  Gtk::Main::run(main_window);
  return (EXIT_SUCCESS);
}

These snippetes have been a re-adapted for this question, so maybe some change is incoherent.

When I execute this code, I have the following output:

$> ./client 
Thread
MyData
Thread
MyData
[...]
Thread
MyData
Thread
MyData
(client:5596): Gtk-CRITICAL **: gtk_text_layout_real_invalidate: assertion 'layout->wrap_loop_count == 0' failed
Thread
MyData
Thread
MyData
[...]
Thread
MyData
Thread
MyData
[1]    5596 segmentation fault (core dumped)  ./client

Can some one help me to resolve this issue ? :)

4

1 回答 1

4

问题是您正在调用线程安全函数调用(信号回调不是线程安全的)。

所以你需要从你的线程中使用类似Glib::signal_idle().connect( sigc::mem_fun(*this, &IdleExample::on_idle) );(或任何相当于 C API call )的东西。g_idle_add(GCallback func)这个函数线程安全的(至少是来自 C API 的那个)。

有关简化示例,请参阅本教程


使用 UI 库时,切勿从不同线程调用或发出信号。通常,API 设计为从单个线程调用。这是使用 UI 工具包时最常犯的一个错误。

于 2014-04-25T11:34:49.053 回答