Безопасно излучать сигнал из потока и подключить его к виджету

Я использую 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

Может ли кто-нибудь помочь мне решить эту проблему? 🙂

0

Решение

Проблема в том, что вы звоните не Потокобезопасный вызов функции (обратные вызовы сигналов не являются поточно-безопасными).

Так что вам нужно использовать что-то вроде Glib::signal_idle().connect( sigc::mem_fun(*this, &IdleExample::on_idle) );(или что-то эквивалентное вызову C API g_idle_add(GCallback func)) из вашей темы. Эта функция является потокобезопасный (по крайней мере, тот из C API).

Видеть это руководство для упрощенного примера.


Никогда не вызывайте и не передавайте сигналы из разных потоков при использовании библиотек пользовательского интерфейса. Обычно API предназначены для вызова из не замужем нить. Это самая частая ошибка при использовании инструментария пользовательского интерфейса.

4

Другие решения


По вопросам рекламы [email protected]