Gtk主循环和GTask

huangapple go评论70阅读模式
英文:

Gtk main loop and GTask

问题

  1. 我是GTK的新手,现在试图找到一个关于GTask如何工作的解释。

  2. 我的主要问题是如何在主线程中实现等待所有任务完成并在退出应用程序之前调用回调函数?

  3. 在调用g_task_run_in_thread后是否需要调用g_main_loop_run,并在每次任务回调中调用g_main_loop_quit

  4. g_main_loop_run会阻塞线程直到调用g_main_loop_quit吗?

  5. 运行多个任务在一个主循环中的最佳方法是什么?

谢谢,大家!

我的测试代码如下:

#include <iostream>
#include <gtk/gtk.h>
#include <gio/gio.h>

typedef GObject myGObj;
static volatile int flag = 0;
GMainLoop *loop;
static int again = 0;

static void some_blocking_function_async (GTask *task,
                              gpointer object,
                              gpointer task_data,
                              GCancellable *cancellable)
{
    printf("%p: %s\n", g_thread_self(), __func__);
    std::cout << " some_blocking_function_async sleep\n";
    usleep(3000000); // 模拟一些工作
    std::cout << " some_blocking_function_async waked up\n";
    // g_main_loop_quit(loop); // 如果在这里退出循环,则不会调用回调

    g_task_return_boolean(task, TRUE);
}

static void cb_func(GObject *gobject,
                    GAsyncResult *res,
                    gpointer      user_data)
{
    printf("%p: %s\n", g_thread_self(), __func__);
    std::cout << "cb_func sleep\n";
    usleep(1000000);
    std::cout << "cb_func waked up\n";
    flag++;
    GTask *task = (GTask*)user_data;

    g_main_loop_quit(loop); // 如果主循环没有退出并再次运行,任务将不会调用回调

    if(flag == 1 && again == 0){
        again++;
        std::cout << "Run 2nd Task\n";
        GTask* task2 = g_task_new(NULL, NULL, cb_func, NULL);
        g_task_run_in_thread(task2, some_blocking_function_async);
        g_object_unref(task2);

        g_main_loop_run(loop); // 如果主循环没有退出并再次运行,任务将不会调用回调
    }
}

int main(int, char**){
    printf("%p: %s\n", g_thread_self(), __func__);
    std::cout << "Hello, from testGt!\n";

    loop = g_main_loop_new(NULL, FALSE);

    GTask* task = g_task_new(NULL, NULL, cb_func, NULL);
    g_task_run_in_thread(task, some_blocking_function_async);
    std::cout << "Task runned, from testGt!\n";
    g_object_unref(task);

    g_main_loop_run(loop);
    std::cout << "Loop runned, from testGt!\n";

    while(flag != 2){
        std::cout << "Sleep, from testGt!\n";
        usleep(1000000);
        std::cout << "Wakeup, from testGt!\n";
    }

    g_main_loop_quit(loop);

    std::cout << flag << " Bye, from testGt!\n";

    return 0;
}

你的CMakeLists.txt文件看起来是正确的,用于构建这个测试应用程序。

英文:

I am newer in GTK and now try to find a explonation of how GTask is working.

  1. My main question is how in main thread I can implement a waiting all tasks finished and callbacks called before exit from app?

  2. Is it nessesary to call g_main_loop_run after g_task_run_in_thread and call g_main_loop_quit in task callback in each time or not?

  3. Is g_main_loop_run block a thread until g_main_loop_quit called?

  4. What is the best way to run many task in one main loop?

Thank you, guys!

My code for test how it is working

#include &lt;iostream&gt;
#include &lt;gtk/gtk.h&gt;
#include &lt;gio/gio.h&gt;
typedef GObject myGObj;
static volatile int flag = 0;
GMainLoop *loop;
static int again = 0;
static void
some_blocking_function_async (GTask *task,
gpointer object,
gpointer task_data,
GCancellable *cancellable)
{
printf(&quot;%p: %s\n&quot;, g_thread_self(), __func__);
std::cout &lt;&lt; &quot; some_blocking_function_async sleep\n&quot;;
usleep(3000000); //emulate some work
std::cout &lt;&lt; &quot; some_blocking_function_async waked up\n&quot;;
//g_main_loop_quit(loop); //callback no called if loop quited here
g_task_return_boolean(task, TRUE);
}
static void 
cb_func(GObject *gobject,
GAsyncResult *res,
gpointer      user_data)
{
printf(&quot;%p: %s\n&quot;, g_thread_self(), __func__);
std::cout &lt;&lt; &quot;cb_func sleep\n&quot;;
usleep(1000000);
std::cout &lt;&lt; &quot;cb_func waked up\n&quot;;
flag++;
GTask *task = (GTask*)user_data;
g_main_loop_quit(loop); //task don&#39;t call a calback if main loop not quited and runned again  ???
if(flag == 1 &amp;&amp; again == 0){
again++;
std::cout &lt;&lt; &quot;Run 2nd Task\n&quot;;
GTask* task2 = g_task_new(NULL, NULL, cb_func, NULL);
g_task_run_in_thread(task2, some_blocking_function_async);
g_object_unref(task2);
g_main_loop_run(loop); //task don&#39;t call a calback if main loop not quited and runned again  ???
}
}
int main(int, char**){
printf(&quot;%p: %s\n&quot;, g_thread_self(), __func__);
std::cout &lt;&lt; &quot;Hello, from testGt!\n&quot;;
loop = g_main_loop_new(NULL, FALSE);
GTask* task = g_task_new(NULL, NULL, cb_func, NULL);
g_task_run_in_thread(task, some_blocking_function_async);
std::cout &lt;&lt; &quot;Task runned, from testGt!\n&quot;;
g_object_unref(task);
g_main_loop_run(loop);
std::cout &lt;&lt; &quot;Loop runned, from testGt!\n&quot;;
while(flag != 2){
std::cout &lt;&lt; &quot;Sleep, from testGt!\n&quot;;
usleep(1000000);
std::cout &lt;&lt; &quot;Wakeup, from testGt!\n&quot;;
}
g_main_loop_quit(loop);
std::cout &lt;&lt; flag &lt;&lt; &quot; Bye, from testGt!\n&quot;;
return 0;
}

Output

0x564375b85c00: main
Hello, from testGt!
Task runned, from testGt!
0x564375b68b60: some_blocking_function_async
some_blocking_function_async sleep
some_blocking_function_async waked up
0x564375b85c00: cb_func
cb_func sleep
cb_func waked up
Run 2nd Task
0x564375b68b60: some_blocking_function_async
some_blocking_function_async sleep
some_blocking_function_async waked up
0x564375b85c00: cb_func
cb_func sleep
cb_func waked up
Loop runned, from testGt!
2 Bye, from testGt!

CMakeLists.txt

cmake_minimum_required(VERSION 3.0.0)
project(testGt VERSION 0.1.0 LANGUAGES C CXX)
include(CTest)
enable_testing()
FIND_PACKAGE(PkgConfig REQUIRED)
PKG_CHECK_MODULES(GTK REQUIRED gtk+-3.0)
INCLUDE_DIRECTORIES(${GTK_INCLUDE_DIRS})
add_executable(testGt main.cpp)
TARGET_LINK_LIBRARIES(testGt ${CMAKE_THREAD_LIBS_INIT} ${GTK_LIBRARIES})
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)

答案1

得分: 1

  1. 你有几种选择,你可以在你生成的任务上调用 g_task_get_completed 来检查它们是否已经完成,但是,由于你是在回调中生成这些任务的,你需要在一个受互斥锁保护的向量中累积生成的任务,例如。

  2. 只有当你希望你的应用一直运行直到退出时才需要调用 g_main_loop_run,否则,如果你已经有自己的循环,你需要使用 g_main_context_iteration 来"驱动" glib 的循环。

  3. 是的,g_main_loop_run 将始终阻塞。

  4. 你可以在循环中创建任务,glib 将负责驱动它们。

在这个代码中,你可以看到每次调用 g_main_context_iteration 时都会驱动任务,当达到所需的运行任务数量时,程序将退出。

英文:

I am not really a glib/gio expert but, I may be able to give some tips.

  1. You have a few options there, you could call g_task_get_completed on the tasks you spawn to check if they are done but, as you are spawning them from the callback, you'd need to accumulate the tasks spawned in a mutex protected vector for example.

  2. You only need to call g_main_loop_run if you want your app to run indefinitely until quit is called otherwise, if you have your own loop already, you need to "drive" glib's loop with g_main_context_iteration.

  3. Yes, g_main_loop_run will always block

  4. You can create the tasks in a loop and glib will take care of driving them.

Here is a changed version of your code that drives glib's loop inside the while statement

--- old.cpp	2023-07-20 09:34:13
+++ a.cpp	2023-07-20 09:34:55
@@ -4,7 +4,6 @@
typedef GObject myGObj;
static volatile int flag = 0;
-GMainLoop *loop;
static int again = 0;
static void
\ No newline at end of file
@@ -32,10 +31,7 @@
usleep(1000000);
std::cout &lt;&lt; &quot;cb_func waked up\n&quot;;
flag++;
-    GTask *task = (GTask *)user_data;
-    g_main_loop_quit(loop); // task don&#39;t call a calback if main loop not quited and runned again  ???
-
if (flag == 1 &amp;&amp; again == 0)
{
again++;
\ No newline at end of file
@@ -43,8 +39,6 @@
GTask *task2 = g_task_new(NULL, NULL, cb_func, NULL);
g_task_run_in_thread(task2, some_blocking_function_async);
g_object_unref(task2);
-
-        g_main_loop_run(loop); // task don&#39;t call a calback if main loop not quited and runned again  ???
}
}
\ No newline at end of file
@@ -53,25 +47,24 @@
printf(&quot;%p: %s\n&quot;, g_thread_self(), __func__);
std::cout &lt;&lt; &quot;Hello, from testGt!\n&quot;;
-    loop = g_main_loop_new(NULL, FALSE);
+    auto loop = g_main_loop_new(NULL, FALSE);
+    auto context = g_main_loop_get_context(loop);
GTask *task = g_task_new(NULL, NULL, cb_func, NULL);
g_task_run_in_thread(task, some_blocking_function_async);
std::cout &lt;&lt; &quot;Task runned, from testGt!\n&quot;;
g_object_unref(task);
-    g_main_loop_run(loop);
std::cout &lt;&lt; &quot;Loop runned, from testGt!\n&quot;;
while (flag != 2)
{
std::cout &lt;&lt; &quot;Sleep, from testGt!\n&quot;;
+        g_main_context_iteration(context, FALSE);
usleep(1000000);
std::cout &lt;&lt; &quot;Wakeup, from testGt!\n&quot;;
}
-    g_main_loop_quit(loop);
-
std::cout &lt;&lt; flag &lt;&lt; &quot; Bye, from testGt!\n&quot;;
return 0;
\ No newline at end of file

On this code, you will see that it drives the tasks every time g_main_context_iteration is called, and when you achieve the desired number of ran tasks, it will just move out and finish the program.

huangapple
  • 本文由 发表于 2023年7月20日 13:14:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76726850.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定