关于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。在这个过程中,程序窗口冻结,进度条窗口不出现(在第一个例子中,这个问题没有发生)。

我该怎么做才能使这个程序不满足这两个问题?

我可以看到您的代码的直接问题是,您正在调用t1_do中的Gtk函数,该函数从次要线程运行,而不将其包含在gtk_thread_enter()中;gtk_thread_leave ();

一般规则是,永远不要从非主UI线程的线程中调用UI函数。除非你解决了这个问题,否则你可能会得到完全随机的错误和行为

最新更新