LINUX.ORG.RU

GTK потоки и вообще

 ,


3

3

У меня 1000 и 1 вопрос по GTK, но самый острый на данный момент это потоки. Читал здесь, здесь, документацию и ещё пару бложиков. Всё равно не осилил. Прошу лоровцев помочь. Вот для примера код:

#include <gtk/gtk.h>

const int BIG_NUM= 9000;

void button1_callback(GtkWidget *button1, gpointer data)
 {
    int i,j,k,l,m;
   
    for(i= 0; i < BIG_NUM; ++i)  
     for(j= 0; j < BIG_NUM; ++j)  
      for(k= 0; k < BIG_NUM; ++k)  
       for(l= 0; l < BIG_NUM; ++l)  
        for(m= 0; m < BIG_NUM; ++m);  
  
 }

void button2_callback(GtkWidget *button2, gpointer data)
 {
     g_printf("Ouch!\n");
 }

int main(int argc, char *argv[])
 {
    GtkWidget *window;
    GtkWidget *button1, *button2;
    GtkWidget *hbox;


    gtk_init(&argc, &argv);

    window= gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_set_size_request(GTK_WIDGET(window), 320, 240);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

    hbox= gtk_hbox_new(FALSE, 0);

    button1= gtk_button_new_with_label("button1");
    button2= gtk_button_new_with_label("button2");

    g_signal_connect(button1, "clicked", G_CALLBACK(button1_callback), NULL);
    g_signal_connect(button2, "clicked", G_CALLBACK(button2_callback), NULL);

    gtk_container_add(GTK_CONTAINER(window), hbox);
    gtk_box_pack_start(GTK_BOX(hbox), button1, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), button2, TRUE, TRUE, 0);

    gtk_widget_show_all(window);


    gtk_main();

    return 0;
 } 
Как сделать, чтоб первая кнопка не «подвешивала» программу? Апгрейд не предлагать.
На данный момент больше вопросов нет, но они обязательно будут и я их озвучу сюда, чтоб не засорять форум. Заранее спасибо.

★★★★

тебе посоветуют потоки, но ты не слушай этих мартышек от ит! сделай конечный автомат, пусть расчеты идут тогда, когда гтк простаивает.

anonymous
()

Я, конечно, не программист, но мне кажется, неправилен сам подход. Интерфейс надо сделать максимально простым и однопоточным. Он должен через неблокирующие механизмы IPC вызывать «бэкенд» в отдельном процессе.

Deleted
()
Ответ на: комментарий от Deleted

Почти так. Вообще не желательно в интерфейсном потоке что-то большое обрабатывать. Иначе интерфейс действительно будет лагать и тормозить.

hibou ★★★★★
()
Ответ на: комментарий от fero

Более подробно:

В void button1_callback(GtkWidget *button1, gpointer data) пишешь pthread_create (...). Эта функция создаст новый поток, который будет выполнятся параллельно гуям. Последняя инструкция в потоке, если нужно что-нить в гуях обносить: gdk_threads_add_idle () с указателями на функцию, которая это сделает.

И помни, ГТК НЕ потокобезопасен.

Rubbiroid
()
Ответ на: комментарий от Rubbiroid

Спасибо. Примеров-то куча, просто они отдельные функции запускают в новый поток, а не функции обрабатывающие события.

fero ★★★★
() автор топика
Ответ на: комментарий от fero

Ну как бы и моем «примере» то же самое. Коллбек (button1_callback) кнопки создает поток (pthread_create), в котором будет выполнятся какая-то функция (параметр к pthread_create). Потом этот поток скажет ГТК «В перерыве выполни эту функцию от своего имени» (gdk_threads_add_idle). И эта последняя функция может нормально обновить гуи.

Не забудь про Race Conditions (хз как по русски) при доступе к данным из разных потоков.

Rubbiroid
()
Ответ на: комментарий от Rubbiroid

Примерно понятно. Попробую. Спасибо ещё раз

fero ★★★★
() автор топика

Вот так вот работает:

#include <gtk/gtk.h>
#include <glib.h>

const int BIG_NUM= 9000;
GMutex *mut;
GThread *thread;


void do_thread () 
{
	 g_printf("The thread has been started.\n");	
    int i,j,k,l,m;
   
    for(i= 0; i < BIG_NUM; ++i)  
     for(j= 0; j < BIG_NUM; ++j)  
      for(k= 0; k < BIG_NUM; ++k)  
       for(l= 0; l < BIG_NUM; ++l)  
        for(m= 0; m < BIG_NUM; ++m);  
	
	g_printf("The thread has been finished.\n");
	g_mutex_unlock (mut);
}

void button1_callback(GtkWidget *button1, gpointer data)
 {
 	if (g_mutex_trylock (mut)) 
 		thread = g_thread_create ((GThreadFunc) do_thread, NULL, FALSE, NULL);
 	else 
 		g_printf("The mutex is already used!\n"); 
 
  
 }

void button2_callback(GtkWidget *button2, gpointer data)
 {
     g_printf("Ouch!\n");
 }

int main(int argc, char *argv[])
 {
    GtkWidget *window;
    GtkWidget *button1, *button2;
    GtkWidget *hbox;

    gtk_init(&argc, &argv);
	 g_thread_init (NULL);

    window= gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_set_size_request(GTK_WIDGET(window), 320, 240);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

    hbox= gtk_hbox_new(FALSE, 0);

    button1= gtk_button_new_with_label("button1");
    button2= gtk_button_new_with_label("button2");

    g_signal_connect(button1, "clicked", G_CALLBACK(button1_callback), NULL);
    g_signal_connect(button2, "clicked", G_CALLBACK(button2_callback), NULL);

    gtk_container_add(GTK_CONTAINER(window), hbox);
    gtk_box_pack_start(GTK_BOX(hbox), button1, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), button2, TRUE, TRUE, 0);

    gtk_widget_show_all(window);


	 mut = g_mutex_new ();


    gtk_main();

    return 0;
 } 

Конечно вариант нуждается в шлифовке, но примерно как-то так.

Собирать вот так:

 
gcc -o gtreads GTreads.c `pkg-config --cflags --libs glib-2.0 gtk+-2.0 gthread-2.0`
hibou ★★★★★
()
Ответ на: комментарий от hibou

Ну за это вообще от души! :) Правда сейчас мне уже не распарсить. Как солнце встанет соберу и буду ковырять.

fero ★★★★
() автор топика

Что люди не делают, лишь не на Qt писать.

bhfq ★★★★★
()
Ответ на: комментарий от AptGet

Согласен. Но делал на скорую руку, чтоб просто хотя бы работало. Нужно доработать.

Тут есть еще одна проблемка в которой я не уверен. После вот этого: thread = g_thread_create (...) создается объект. Но его наверное нужно еще и уничтожать, когда он не нужен? В смысле, unref. Или же он автоматом уничтожается сам?

hibou ★★★★★
()

Почему и зачем GTK?

anonymous
()
Ответ на: комментарий от Zhbert

:) ну мне кажется, это мало зависит от инструмента.

Вообще я сам редко пользуюсь дополнительными тредами. Просто мало таких задач. Чаще использую idle-функции. Они исполняются в том же потоке, что и интерфейс, но только когда gtk находится в цикле ожидания и ничего другого не делает. Для повседневных задач этого хватает. Интерфейс не подвешивается и накладных расходов на дополнительный тред нет.

hibou ★★★★★
()
Ответ на: комментарий от hibou

Ваш листинг собрал, посмотрел. Всё замечательно. Благодарю. Теперь вот про idle-функции читаю. Даже не догадывался об их существовании.

fero ★★★★
() автор топика
Ответ на: комментарий от fero

Выбор idle-функция или параллельный тред - зависит от типа задачи. Idle хороши для разных мелких задачек. Или же когда одну большую можно разбить на несколько маленьких периодических. Например, вот как добавление картинок в галерею. Периодически по одной так добавлять в фоне. Если же задача большая, вот как у вас, большой цикл, то наверное лучше дополнительный тред.

Помните, idle исполняются в интерфейсном потоке. Если там считать что-то большое и долгое, интерфейс будет подвисать.

hibou ★★★★★
()

Совершенно не в тему, но может знает кто есть ли аналоги noecho() и cbreak() из ncurses в стандартной библиотеке Си?

fero ★★★★
() автор топика
Ответ на: комментарий от fero

Undefined behavior, работоспособность такого решения не гарантируется.

AptGet ★★★
()
Ответ на: комментарий от hibou

Просто я впервые про такую «концепцию» слышу, про потоки слышал, и даже их использовал, а про Idle функции нет, вот и думаю, что же это такое )

xterro ★★★★★
()
Последнее исправление: xterro (всего исправлений: 1)
Ответ на: комментарий от xterro

в gtk это callback-функция, которая посредством вызова g_idle_add регистрируется в главном цикле gtk, и вызывается после вызова всех обработчиков events, пока не вернет FALSE. позволяет вызывать gtk-функции из другого потока без использования мутексов (соотв. уменьшает вероятность race conditions). в других тулкитах есть аналогичные вещи, но называются и работают немного иначе. к примеру, в андроидном тулките используется класс android.os.Handler, который делает примерно то же самое.

waker ★★★★★
()
Ответ на: комментарий от hibou

насчет этого не знаю, не пользовался.

waker ★★★★★
()
Ответ на: комментарий от hibou
#include <gtk/gtk.h>
#include <glib.h>

const int BIG_NUM= 9000;
GMutex *mut;
GThread *thread;


void do_thread () 
{
	 g_printf("The thread has been started.\n");	
    int i,j,k,l,m;
   
    if (!g_mutex_trylock (mut)) 
     {
        g_printf("The mutex is already used!\n"); 
        return;
     }

    for(i= 0; i < BIG_NUM; ++i);  
     for(j= 0; j < BIG_NUM; ++j)  
      for(k= 0; k < BIG_NUM; ++k)  
       for(l= 0; l < BIG_NUM; ++l)  
        for(m= 0; m < BIG_NUM; ++m);  
	
	g_printf("The thread has been finished.\n");
	g_mutex_unlock (mut);
}

void button1_callback(GtkWidget *button1, gpointer data)
 {
     thread = g_thread_create ((GThreadFunc) do_thread, NULL, FALSE, NULL);
     
 }

void button2_callback(GtkWidget *button2, gpointer data)
 {
     g_printf("Ouch!\n");
 }

int main(int argc, char *argv[])
 {
    GtkWidget *window;
    GtkWidget *button1, *button2;
    GtkWidget *hbox;

    gtk_init(&argc, &argv);
	 g_thread_init (NULL);

    window= gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_set_size_request(GTK_WIDGET(window), 320, 240);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

    hbox= gtk_hbox_new(FALSE, 0);

    button1= gtk_button_new_with_label("button1");
    button2= gtk_button_new_with_label("button2");

    g_signal_connect(button1, "clicked", G_CALLBACK(button1_callback), NULL);
    g_signal_connect(button2, "clicked", G_CALLBACK(button2_callback), NULL);

    gtk_container_add(GTK_CONTAINER(window), hbox);
    gtk_box_pack_start(GTK_BOX(hbox), button1, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), button2, TRUE, TRUE, 0);

    gtk_widget_show_all(window);


	 mut = g_mutex_new ();


    gtk_main();

    return 0;
 } 

Засунул блокировку/разблокировку мьютекса в один тред, как и говорилось выше. Я так понимаю, что сейчас у меня память течет из-за того, что я не освобождаю GThread *thread, но создаю новые объекты тыкая на кнопку?
Пытался разрулить самостоятельно, для этого делал join, но интерфейс повисает до завершения выполнения do_thread(), поэтому смысла в этом способе не вижу. С unref не могу разобраться. Как всегда :)

fero ★★★★
() автор топика
Ответ на: комментарий от fero

Нда.... Мне кажется, так еще корявее. У тебя тред создается в любом случае, даже если мутекс захвачен. Насчет памяти, да, наверное. Нужно побольше почитать, как ее освобождать.

hibou ★★★★★
()
Ответ на: комментарий от hibou

Если я не ошибаюсь, тред создаёт ссылку сам на себя когда запускается, поэтому можно в нём сделать:

self= g_thread_self();
g_thread_unref(self);
Удаляем свою ссылку и дело в шляпе! Он удалит ссылку которую вставил сам после завершения. Но! Я не могу собрать листинг:
thread.c:(.text+0x9b): undefined reference to `g_thread_unref'
collect2: выполнение ld завершилось с кодом возврата 1

fero ★★★★
() автор топика
Последнее исправление: fero (всего исправлений: 1)
Ответ на: комментарий от fero

Проблема с компиляцией решена обновлением библиотек. Код на данный момент выглядит так:

#include <gtk/gtk.h>
#include <glib.h>

const int BIG_NUM= 9000;
GMutex *mut;
GThread *thread;


void do_thread () 
{

    g_printf("The thread has been started.\n");	
    int i,j,k,l,m;

    GThread *self;

    self= g_thread_self();
   
    if (!g_mutex_trylock (mut)) 
     {
        g_thread_unref(self);
        g_printf("The mutex is already used!\n"); 
        return;
     }

    for(i= 0; i < BIG_NUM; ++i);  
     for(j= 0; j < BIG_NUM; ++j)  
      for(k= 0; k < BIG_NUM; ++k)  
       for(l= 0; l < BIG_NUM; ++l)  
        for(m= 0; m < BIG_NUM; ++m);  
	
	g_printf("The thread has been finished.\n");
	g_mutex_unlock (mut);
        g_thread_unref(self);
}

void button1_callback(GtkWidget *button1, gpointer data)
 {
     thread = g_thread_create ((GThreadFunc) do_thread, NULL, FALSE, NULL);
     
 }

void button2_callback(GtkWidget *button2, gpointer data)
 {
     g_printf("Ouch!\n");
 }

int main(int argc, char *argv[])
 {
    GtkWidget *window;
    GtkWidget *button1, *button2;
    GtkWidget *hbox;

    gtk_init(&argc, &argv);
	 g_thread_init (NULL);

    window= gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_widget_set_size_request(GTK_WIDGET(window), 320, 240);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

    hbox= gtk_hbox_new(FALSE, 0);

    button1= gtk_button_new_with_label("button1");
    button2= gtk_button_new_with_label("button2");

    g_signal_connect(button1, "clicked", G_CALLBACK(button1_callback), NULL);
    g_signal_connect(button2, "clicked", G_CALLBACK(button2_callback), NULL);

    gtk_container_add(GTK_CONTAINER(window), hbox);
    gtk_box_pack_start(GTK_BOX(hbox), button1, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), button2, TRUE, TRUE, 0);

    gtk_widget_show_all(window);


	 mut = g_mutex_new ();


    gtk_main();

    return 0;
 } 
Освобождается ли память выделяемая здесь:
thread = g_thread_create ((GThreadFunc) do_thread, NULL, FALSE, NULL);
?
Постарался учесть все замечания, которые озвучены выше. Но получилось как-то коряво. Что ещё посоветуете?

fero ★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.