Проблема с GThread и копией файла - PullRequest
1 голос
/ 23 июля 2011
// gcc -o 0 $(pkg-config --cflags --libs gtk+-2.0) 1.c
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
struct tst
{
    GtkWidget *win, *w2, *hb, *vb, *ent, *btn, *b2, *pbar;
    GtkAccelGroup *acc;
};
GCancellable *can1;
GError *err1;
GThread *t1;
static void t1_stop (struct tst *prg)
{
    g_cancellable_cancel (can1);
    can1 = NULL;
}
gpointer t1_do (gpointer ptr1)
{
    struct tst *prg = (gpointer)ptr1;
    g_file_copy (g_file_new_for_path ("/1.avi"), g_file_new_for_path ("/2.avi"), G_FILE_COPY_NOFOLLOW_SYMLINKS, can1, NULL, NULL, &err1);
    if (err1 != NULL) g_error_free (err1);
    gtk_widget_destroy (prg->w2);
}
static void window_pbar (struct tst *prg)
{
    prg->w2 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    prg->hb = gtk_hbox_new (FALSE, 0);
    prg->pbar = gtk_progress_bar_new ();
    prg->b2 = gtk_button_new_with_label ("Cancel");
    gtk_container_add (GTK_CONTAINER (prg->w2), GTK_WIDGET (prg->hb));
    gtk_window_set_position (GTK_WINDOW (prg->w2), GTK_WIN_POS_CENTER);
    gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->pbar), FALSE, FALSE, 0);
    gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->b2), FALSE, FALSE, 0);
    g_signal_connect_swapped (prg->w2, "delete_event", G_CALLBACK (t1_stop), prg);
    g_signal_connect_swapped (prg->b2, "clicked", G_CALLBACK (t1_stop), prg);
    gtk_widget_show_all (GTK_WIDGET (prg->w2));
    can1 = g_cancellable_new ();
    err1 = NULL;
    t1 = g_thread_create (t1_do, (gpointer)prg, TRUE, NULL);
}
static void window_new ()
{
    struct tst *prg = g_new0 (struct tst, 1);
    prg->win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    prg->vb = gtk_vbox_new (FALSE, 0);
    prg->btn = gtk_button_new_with_label ("start");
    gtk_container_add (GTK_CONTAINER (prg->win), GTK_WIDGET (prg->vb));
    gtk_box_pack_start (GTK_BOX (prg->vb), GTK_WIDGET (prg->btn), FALSE, FALSE, 0);
    g_signal_connect (prg->win, "delete_event", G_CALLBACK (gtk_main_quit), NULL);
    g_signal_connect_swapped (prg->btn, "clicked", G_CALLBACK (window_pbar), prg);
    gtk_window_set_title (GTK_WINDOW (prg->win), "Test program");
    gtk_window_set_position (GTK_WINDOW (prg->win), GTK_WIN_POS_CENTER);
    gtk_widget_show_all (GTK_WIDGET (prg->win));
    gtk_main ();
}
int main (int argc, char *argv[])
{
    gtk_init (&argc, &argv);
    window_new ();
    return 0;
}

Эта программа является примером. При нажатии кнопки «Пуск» программа создает окно с индикатором выполнения и кнопкой «Отмена» и создает поток для копирования /1.avi в /2.avi, но из-за того, что /1.avi не существует, программа напишет «Ошибка!» на терминале и закройте окно индикатора выполнения. Но есть одна проблема в этой программе. Когда я нажимаю кнопку «Пуск» много раз, когда программа пишет очередные сообщения об ошибках на терминале. Иногда сообщения о GDK, иногда о GObject, а иногда о GTK +. А иногда сама программа зависает или падает.

// gcc -o 0 $(pkg-config --cflags --libs gtk+-2.0) 1.c
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
struct tst
{
    GtkWidget *win, *w2, *hb, *vb, *ent, *btn, *b2, *pbar;
    GtkAccelGroup *acc;
};
GCancellable *can1;
GError *err1;
GThread *t1;
static void t1_stop (struct tst *prg)
{
    g_cancellable_cancel (can1);
    can1 = NULL;
}
gpointer t1_do (gpointer ptr1)
{
    struct tst *prg = (gpointer)ptr1;
    g_file_copy (g_file_new_for_path ("/1.avi"), g_file_new_for_path ("/2.avi"), G_FILE_COPY_NOFOLLOW_SYMLINKS, can1, NULL, NULL, &err1);
    if (err1 != NULL) g_error_free (err1);
    gtk_widget_destroy (prg->w2);
}
static void window_pbar (struct tst *prg)
{
    prg->w2 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    prg->hb = gtk_hbox_new (FALSE, 0);
    prg->pbar = gtk_progress_bar_new ();
    prg->b2 = gtk_button_new_with_label ("Cancel");
    gtk_container_add (GTK_CONTAINER (prg->w2), GTK_WIDGET (prg->hb));
    gtk_window_set_position (GTK_WINDOW (prg->w2), GTK_WIN_POS_CENTER);
    gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->pbar), FALSE, FALSE, 0);
    gtk_box_pack_start (GTK_BOX (prg->hb), GTK_WIDGET (prg->b2), FALSE, FALSE, 0);
    g_signal_connect_swapped (prg->w2, "delete_event", G_CALLBACK (t1_stop), prg);
    g_signal_connect_swapped (prg->b2, "clicked", G_CALLBACK (t1_stop), prg);
    gtk_widget_show_all (GTK_WIDGET (prg->w2));
    can1 = g_cancellable_new ();
    err1 = NULL;
    t1 = g_thread_create (t1_do, (gpointer)prg, TRUE, NULL);
    g_thread_join (t1);
}
static void window_new ()
{
    struct tst *prg = g_new0 (struct tst, 1);
    prg->win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    prg->vb = gtk_vbox_new (FALSE, 0);
    prg->btn = gtk_button_new_with_label ("start");
    gtk_container_add (GTK_CONTAINER (prg->win), GTK_WIDGET (prg->vb));
    gtk_box_pack_start (GTK_BOX (prg->vb), GTK_WIDGET (prg->btn), FALSE, FALSE, 0);
    g_signal_connect (prg->win, "delete_event", G_CALLBACK (gtk_main_quit), NULL);
    g_signal_connect_swapped (prg->btn, "clicked", G_CALLBACK (window_pbar), prg);
    gtk_window_set_title (GTK_WINDOW (prg->win), "Test program");
    gtk_window_set_position (GTK_WINDOW (prg->win), GTK_WIN_POS_CENTER);
    gtk_widget_show_all (GTK_WIDGET (prg->win));
    gtk_main ();
}
int main (int argc, char *argv[])
{
    gtk_init (&argc, &argv);
    window_new ();
    return 0;
}

Итак, я изменил некоторые части программы. Теперь проблема не возникает, хотя я нажимаю кнопку «Пуск» много раз, но я столкнулся с другой проблемой. Когда я могу скопировать /1.avi в /2.avi, если я нажму кнопку «Пуск», то программа скопирует /1.avi в /2.avi, но во время этого процесса окно программы зависнет, и окно индикатора выполнения не появится (В первом примере эта проблема не возникает).

Что я должен сделать, чтобы эта программа не встретила ни одной из двух проблем?

1 Ответ

2 голосов
/ 30 июля 2011

Непосредственная проблема, которую я вижу в вашем коде, заключается в том, что вы вызываете функцию Gtk в t1_do, которая запускается из вторичного потока без включения ее в gtk_thread_enter ();gtk_thread_leave ();

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

...