Проблема с GThread и копией файла

// 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 ответ

Решение

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

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

Другие вопросы по тегам