英文:
Gtk main loop and GTask
问题
-
我是GTK的新手,现在试图找到一个关于GTask如何工作的解释。
-
我的主要问题是如何在主线程中实现等待所有任务完成并在退出应用程序之前调用回调函数?
-
在调用
g_task_run_in_thread
后是否需要调用g_main_loop_run
,并在每次任务回调中调用g_main_loop_quit
? -
g_main_loop_run
会阻塞线程直到调用g_main_loop_quit
吗? -
运行多个任务在一个主循环中的最佳方法是什么?
谢谢,大家!
我的测试代码如下:
#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.
-
My main question is how in main thread I can implement a waiting all tasks finished and callbacks called before exit from app?
-
Is it nessesary to call
g_main_loop_run
afterg_task_run_in_thread
and callg_main_loop_quit
in task callback in each time or not? -
Is
g_main_loop_run
block a thread untilg_main_loop_quit
called? -
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 <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); //emulate some work
std::cout << " some_blocking_function_async waked up\n";
//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("%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); //task don't call a calback if main loop not quited and runned again ???
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); //task don't call a calback if main loop not quited and runned again ???
}
}
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;
}
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
-
你有几种选择,你可以在你生成的任务上调用
g_task_get_completed
来检查它们是否已经完成,但是,由于你是在回调中生成这些任务的,你需要在一个受互斥锁保护的向量中累积生成的任务,例如。 -
只有当你希望你的应用一直运行直到退出时才需要调用
g_main_loop_run
,否则,如果你已经有自己的循环,你需要使用g_main_context_iteration
来"驱动" glib 的循环。 -
是的,
g_main_loop_run
将始终阻塞。 -
你可以在循环中创建任务,glib 将负责驱动它们。
在这个代码中,你可以看到每次调用 g_main_context_iteration
时都会驱动任务,当达到所需的运行任务数量时,程序将退出。
英文:
I am not really a glib/gio expert but, I may be able to give some tips.
-
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. -
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 withg_main_context_iteration
. -
Yes,
g_main_loop_run
will always block -
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 << "cb_func waked up\n";
flag++;
- GTask *task = (GTask *)user_data;
- g_main_loop_quit(loop); // task don't call a calback if main loop not quited and runned again ???
-
if (flag == 1 && 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't call a calback if main loop not quited and runned again ???
}
}
\ No newline at end of file
@@ -53,25 +47,24 @@
printf("%p: %s\n", g_thread_self(), __func__);
std::cout << "Hello, from testGt!\n";
- 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 << "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";
+ g_main_context_iteration(context, FALSE);
usleep(1000000);
std::cout << "Wakeup, from testGt!\n";
}
- g_main_loop_quit(loop);
-
std::cout << flag << " Bye, from testGt!\n";
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论