安全地发出来自线程的信号并将其连接到小部件

时间:2014-04-24 22:35:17

标签: c++ multithreading gtk signals glibmm

我使用Gtkmm和多线程。

我有一个班级" NetworkWorker" doig在辅助线程中填充网络。 在这堂课中,我想制作许多信号,这些信号将由我的班级" MainWindow"处理。

处理这些信号的方法将编辑TextView中的附加文本。

我有以下代码:

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());
}

和我的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);
}

这些片段已经重新适应了这个问题,所以也许某些变化是不连贯的。

当我执行此代码时,我有以下输出:

$> ./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

有人可以帮我解决这个问题吗? :)

1 个答案:

答案 0 :(得分:4)

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

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

有关简化示例,请参阅此tutorial


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