如何在不阻塞 UI 的情况下更新 Vala 中的 GTK+ UI 进行长时间操作

g_l*_*g_l 1 multithreading vala gtk3

当我使用此页面中的任何代码而不修改任何内容时:https://wiki.gnome.org/Projects/Vala/AsyncSamples

\n\n

我总是得到:\nwarning: \xe2\x80\x98g_simple_async_result_new\xe2\x80\x99 is deprecated: Use \'g_task_new\' instead.

\n\n

所以我继续建议使用GTask。然而,当我尝试在 Vala 中使用 GLib.Task 时,我在声明任务时陷入困境。因此,我没有在自己的代码中使用 GIO 的异步(因为它已被弃用),而是尝试使用 GLib.Task 来简单地使用 for 循环中的数字更新 Gtk 按钮的标签,这样代码如下所示:

\n\n
using Gtk;\nButton button;\n\npublic static int main (string[] args) {\n    Gtk.init (ref args);\n\n    var window = new Window ();\n    window.title = "Count without blocking the UI";\n    window.border_width = 10;\n    window.window_position = WindowPosition.CENTER;\n    window.set_default_size (350, 70);\n    window.destroy.connect (Gtk.main_quit);\n    button = new Button.with_label ("Start counting");\n    button.clicked.connect (() => {\n    GLib.Task task = new GLib.Task(button, new Cancellable());\n});\n\nwindow.add (button);\nwindow.show_all ();\n\nGtk.main ();\n\nreturn 0;\n}\n\nvoid count(){\n    for(int i = 0; i < 10000; i++){\n        button.label = i.to_string();\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

但是编译时我得到:error: \xe2\x80\x98_data_\xe2\x80\x99 undeclared (first use in this function)\n _tmp3_ = g_task_new_finish (_data_->_res_);

\n\n

第 15 行是导致编译器抛出该错误的原因。它来自vala编译器生成的C代码。

\n\n

我发现的主要问题是 Vala 中的 GTask 构造函数签名与 C 不同。因此,我无法重新创建此处找到的代码:GUI gets unresponsive after click the button using GTK+ in C

\n\n

因为对于初学者来说,我不允许将两个以上的参数传递给 GLib.Task 对象构造函数。每种语言中 Task 对象的构造函数都不同。Vala 中 GLib.Task 的构造函数可以在这里找到。

\n\n

因此我的问题是:

\n\n

有没有关于如何在 Vala 中使用 GLib Task (GTask) 来执行更新 UI 而不阻塞 UI 的操作的示例?如果没有,是否有其他方法可以在不阻止它的情况下更新 UI?一种不被弃用的方式?

\n\n

谢谢。

\n\n

PS:我已经尝试过 GLib.Thread、GLib.ThreadPool 和 GLib.Idle。它们都会在 for 循环中阻塞 UI。GLib.Idle 不会完全阻塞 UI,但它会使其出现错误,因为在循环运行时响应用户输入变得非常慢。

\n

art*_*mix 5

使用异步是完全可以的,并且已经有一些工作可以将当前代码移植到使用 GTask。

您的计数代码是阻塞的,因此即使使用 GTask 缓冲其执行,它仍然会阻塞 UI。

执行 CPU 密集型后台操作的正确方法是异步使用子进程或在线程中启动工作并在主循环中分派。

async void test_async () {
    new Thread<void> (() => {
         // count here...
        test_async.callback ();
    });
    yield;
}
Run Code Online (Sandbox Code Playgroud)

GTask或更一般的GAsyncResult仅提供用于保存异步操作结果的容器。他们还建议使用 GThreadPool,但它有点样板。

另一个有趣的事情是它test_async.callback实际上是 a SourceFunc,所以你可以将它传递给GLib.Timeout.

编辑:

为了更适合您的问题,如果您想在 UI 进行时更新 UI,请使用异步循环:

async test_callback () {
    for (var i = 0; i < 10000; i++) {
        button.label = i.to_string ();
        Idle.add (test_async.callback);
        yield; // pause execution until retriggered in idle
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个完整且有效的示例:

using Gtk;
Button button;

public static int main (string[] args) {
    Gtk.init (ref args);

    var window = new Window ();
    window.title = "Count without blocking the UI";
    window.border_width = 10;
    window.window_position = WindowPosition.CENTER;
    window.set_default_size (350, 70);
    window.destroy.connect (Gtk.main_quit);
    button = new Button.with_label ("Start counting");
    button.clicked.connect (() => {
        count ();
    });

    window.add (button);
    window.show_all ();

    Gtk.main ();

    return 0;
}

async void count(){
    for(int i = 0; i < 10000; i++){
        button.label = i.to_string();
        Idle.add (count.callback);
        yield;
    }
}
Run Code Online (Sandbox Code Playgroud)