c++ - 从线程安全地发出信号并将其连接到小部件

标签 c++ multithreading gtk signals glibmm

我正在使用 Gtkmm 和多线程。

我有一个“NetworkWorker”类在辅助线程中处理网络。 在这个类中,我想发出许多信号,这些信号将由我的类“MainWindow”处理。

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

我有以下代码:

网络 worker .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);
    }
}

主窗口.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

主窗口.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

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

最佳答案

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

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

查看此 tutorial一个简化的例子。


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

关于c++ - 从线程安全地发出信号并将其连接到小部件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23281110/

相关文章:

c++ - 使用codecave注入(inject)线程

C++: vector 的子 vector 作为函数的参数

c++ - 保留 `unique_ptr` 未设置,直到我准备好分配它 : Is this how I re-assign it?

c# - 如何显示另一个UI线程上的对话框

python - 如何使对话框后面的窗口不可点击?

c - 使用 GtkClipboard 获取 URL

c++ - Eclipse、C++ 和 Mysql

linux - 用户线程和内核线程与线程级库和内核级库是否有以下任何一种对应关系?

java 线程 多线程

python - 回调函数中的 AttributeError