You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言结合GTK3如何实现不中断运行的线程以完成骰子滚动动画?

核心问题原因
  • GTK 框架的所有GUI操作接口仅允许在运行GTK主循环(gtk_main)的主线程中调用,本身不支持跨线程操作GUI。你在自定义的pthread线程中直接调用gtk_widget_set_sensitive、gtk_image_set_from_pixbuf修改控件状态,完全违反了GTK的线程安全约定,这就是程序随机崩溃的根本原因,和是否加互斥锁无关。
  • 额外隐患:连续点击Roll按钮会重复创建线程,覆盖全局的thread变量,导致之前创建的线程资源泄漏、无法正常回收。
修复方案

不需要自行创建线程,直接使用GTK内置的定时回调接口g_timeout_add即可实现间隔触发的需求,定时回调默认在主线程执行,可以安全操作所有GUI控件,代码更简洁也没有线程安全问题。

修正后完整代码
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

GtkWidget *window;
GtkWidget *vbox;
GtkWidget *butt;
GtkWidget *image;
GdkPixbuf *pixbuf1;
GdkPixbuf *pixbuf2;
GdkPixbuf *pixbuf3;
GdkPixbuf *pixbuf4;
GdkPixbuf *pixbuf5;
GdkPixbuf *pixbuf6;

int roll_count = 0;
guint timer_id = 0;

// 定时回调函数,每次返回G_SOURCE_CONTINUE继续执行,返回G_SOURCE_REMOVE销毁定时器
gboolean roll_dice_cb(gpointer user_data)
{
    if (roll_count >= 4) {
        // 滚动结束,恢复按钮可用,销毁定时器
        gtk_widget_set_sensitive(butt, TRUE);
        timer_id = 0;
        return G_SOURCE_REMOVE;
    }

    int j = rand() % 6 + 1;
    switch (j)
    {
        case 1: gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf1); break;
        case 2: gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf2); break;
        case 3: gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf3); break;
        case 4: gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf4); break;
        case 5: gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf5); break;
        default: gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf6);
    }

    roll_count++;
    return G_SOURCE_CONTINUE;
}

void start_roll(GtkWidget *widget, gpointer user_data)
{
    // 避免重复触发
    if (timer_id != 0) return;

    gtk_widget_set_sensitive(butt, FALSE);
    roll_count = 0;
    // 注册400ms触发一次的定时器
    timer_id = g_timeout_add(400, roll_dice_cb, NULL);
}

int main (int argc, char *argv[])
{
    gtk_init(&argc, &argv);

    srand(time(0));

    pixbuf1 = gdk_pixbuf_new_from_file("dice_1_48x48.png", NULL);
    pixbuf2 = gdk_pixbuf_new_from_file("dice_2_48x48.png", NULL);
    pixbuf3 = gdk_pixbuf_new_from_file("dice_3_48x48.png", NULL);
    pixbuf4 = gdk_pixbuf_new_from_file("dice_4_48x48.png", NULL);
    pixbuf5 = gdk_pixbuf_new_from_file("dice_5_48x48.png", NULL);
    pixbuf6 = gdk_pixbuf_new_from_file("dice_6_48x48.png", NULL);

    image = gtk_image_new();
    gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf1);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
    gtk_window_set_default_size(GTK_WINDOW(window), 150, 100);

    butt = gtk_button_new_with_label("Roll");
    g_signal_connect(G_OBJECT(butt), "clicked", G_CALLBACK(start_roll), NULL);

    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL,10);
    gtk_box_pack_start(GTK_BOX(vbox),image,0,0,0);
    gtk_box_pack_start(GTK_BOX(vbox),butt,0,0,0);
    gtk_container_add(GTK_CONTAINER(window), vbox);
    gtk_widget_show_all(window);

    gtk_main();

    // 退出前如果定时器还在运行就销毁
    if (timer_id != 0) {
        g_source_remove(timer_id);
    }

    // 释放pixbuf资源
    g_object_unref(pixbuf1);
    g_object_unref(pixbuf2);
    g_object_unref(pixbuf3);
    g_object_unref(pixbuf4);
    g_object_unref(pixbuf5);
    g_object_unref(pixbuf6);

    return 0;
}

编译指令保持不变:
gcc -rdynamic -o test3d1 test3d1.c $(pkg-config --cflags --libs gtk+-3.0)

内容的提问来源于stack exchange,提问作者Graeme Jensz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 00:48:00