十三、GIO GTask

GTask是用于管理可取消任务的工具,常用于异步操作。它与GCancellable配合,提供线程安全的取消机制。g_task_new用于创建GTask,g_task_run_in_thread则在另一线程中执行任务,完成后在GMainContext中调用回调。文章展示了如何在C程序中使用GTask进行异步蛋糕烘焙操作,通过g_task_set_task_data设置和管理任务数据。

GTask表示管理一个可取消的“任务task”

GCancellable

GCancellable是一个线程安全的操作取消栈,用于整个GIO,以允许取消同步和异步操作。

它继承于GObject对象,不是一个单纯的结构体

相关函数

g_task_new

GTask*
g_task_new (
  GObject* source_object,
  GCancellable* cancellable,
  GAsyncReadyCallback callback,
  gpointer callback_data
)

创建一个作用于source_object的GTask,它最终会在[thread-default main context][g-main-context-push-thread-default]中被使用去调用callback

在异步方法的“start”方法中调用它,并在异步操作中传递GTask。可以使用g_task_set_task_data()将特定于任务的数据附加到对象上,稍后可以通过g_task_get_task_data()获取这些数据。

默认情况下,如果cancelable被取消,那么任务的返回值总是G_IO_ERROR_CANCELLED,即使任务在取消之前已经完成。取消操作可能意味着该任务所依赖的其他对象已经被销毁,这种情况下的处理可以简化。如果你不想要这种行为,可以使用g_task_set_check_cancellable()来改变它。

g_task_set_task_data

void
g_task_set_task_data (
  GTask* task,
  gpointer task_data,
  GDestroyNotify task_data_destroy
)

设定task任务的数据

g_task_run_in_thread

void
g_task_run_in_thread (
  GTask* task,
  GTaskThreadFunc task_func
)

在另一个线程中运行task_func。当task_func返回时,任务的GAsyncReadyCallback将在任务的GMainContext中调用(任务实在那个GMainContext上下文中创建的,就那个上下文中执行回调函数)。

这里会对task进行一个ref,直到task完成。

有关如何处理task_func的更多细节,请参阅GTaskThreadFunc。

尽管GLib当前对通过g_task_run_in_thread()排队的任务进行速率限制,但大家不应该假定它总是这样做。如果你有很多任务要运行(几十个任务),但又不希望它们同时运行,那么一次只应该将有限数量的任务(大约10个)放入队列。

#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <stdio.h>
#include <stdlib.h>

typedef int CakeFlavor;
typedef int CakeFrostingType;

typedef struct {
    guint radius; /* 蛋糕半径 */
    CakeFlavor flavor; /* 蛋糕风味 */
    CakeFrostingType frosting; /* 蛋糕糖霜类型 */
    char *message;
} CakeData;

typedef GObject Cake;
typedef GObject Baker; /* 面包师 */

static void
cake_data_free (CakeData *cake_data) {
    g_print ("%p: %s\n", g_thread_self(), __func__);
    g_free (cake_data->message);
    /*  */
    g_slice_free (CakeData, cake_data);
}

/* 烤面包 */
static Cake *
bake_cake(Baker *self, guint radius, CakeFlavor flavor,
		  CakeFrostingType frosting, char *message,
		  GCancellable *cancellable, GError **error)
{
	printf("%p: %s\n", g_thread_self(), __func__);

	return g_object_new(G_TYPE_OBJECT, NULL);
}

static void 
bake_cake_thread(GTask *task, gpointer source_object,
			     gpointer task_data, GCancellable *cancellable)
{
	Baker *self = source_object;
	CakeData *cake_data = task_data;
	Cake *cake;
	GError *error = NULL;

	printf("%p: %s\n", g_thread_self(), __func__);

	cake = bake_cake(self, cake_data->radius, cake_data->flavor,
			 cake_data->frosting, cake_data->message, cancellable,
			 &error);
	if (cake)
		g_task_return_pointer(task, cake, g_object_unref);
	else
		g_task_return_error(task, error);
}

static void 
baker_bake_cake_async(Baker *self, guint radius, CakeFlavor flavor,
				      CakeFrostingType frosting,
                      const char *message,
				      GCancellable *cancellable,
				      GAsyncReadyCallback callback,
				      gpointer user_data) {
	CakeData *cake_data;
	GTask *task;

	printf("%p: %s\n", g_thread_self(), __func__);

    /* 切片分配内存(这是一个便利的宏,从slice allocator分配内存) */
	cake_data = g_slice_new(CakeData);
	cake_data->radius = radius;
	cake_data->flavor = flavor;
	cake_data->frosting = frosting;
	cake_data->message = g_strdup(message);

	task = g_task_new(self, cancellable, callback, user_data);
    /* 把数据附加在task上 */
	g_task_set_task_data(task, cake_data, (GDestroyNotify)cake_data_free);
	g_task_run_in_thread(task, bake_cake_thread);

	g_object_unref(task);
}


static Cake *
baker_bake_cake_finish(Baker *self, GAsyncResult *res,
				       GError **error) {
	g_return_val_if_fail(g_task_is_valid(res, self), NULL);

	printf("%p: %s\n", g_thread_self(), __func__);

	return g_task_propagate_pointer(G_TASK(res), error);
}

static void 
my_callback(GObject *source_object, GAsyncResult *res,
			gpointer user_data) {
    /* 创建一个面包师 */
	Baker *baker = (Baker *)source_object;
	GMainLoop *loop = (GMainLoop *)user_data;
	Cake *cake;
	GError *error = NULL;

	printf("%p: %s\n", g_thread_self(), __func__);

	cake = baker_bake_cake_finish(baker, res, &error);

	printf("A cake is baked: %p\n", cake);

	// But discard it. I prefer pudding.
	g_object_unref(cake);

	// Stop cooking.
	g_main_loop_quit(loop);
}

int 
main(void) {
	Baker *baker = g_object_new(G_TYPE_OBJECT, NULL);
	GCancellable *cancellable = g_cancellable_new();

	GMainLoop *loop = g_main_loop_new(NULL, FALSE);
    printf("%p: %s\n", g_thread_self(), __func__);
	baker_bake_cake_async(baker, 10, 20, 30, "emit", cancellable,
			      1, loop);
	g_object_unref(cancellable);

	printf("%p: start event loop.\n", g_thread_self());
	g_main_loop_run(loop);

	g_main_loop_unref(loop);
	g_object_unref(baker);

	return EXIT_SUCCESS;
}

异步操作

GTask最常见的用法是作为一个GAsyncResult,在异步操作期间管理数据。你可以在“start”方法中调用g_task_new(),然后调用g_task_set_task_data()以及类似的方法,如果你需要保留一些与任务相关的额外数据,然后通过异步操作传递任务对象。最终,您将调用诸如g_task_return_pointer()或g_task_return_error()之类的方法,该方法将保存您给它的值,然后在创建它的[thread-default main context][g-main-context-push-thread-default]中调用任务的回调函数(如果需要,首先等待主循环的下一次迭代)。调用者将把GTask传递回操作的finish函数(作为GAsyncResult),你可以使用g_task_propagate_pointer()或类似的函数来提取返回值。

使用GTask要求thread-default的GMainContext从GTask构建时开始运行,至少直到任务完成并释放其数据。

下面是一个使用GTask作为GAsyncResult的例子:

<think>我们正在解决如何在CMake中正确链接GIO库的问题。GIO是GLib的一部分,提供了高级的I/O功能,包括D-Bus支持(GDBus)。以下步骤将指导您完成配置。 ### 步骤1: 确保系统已安装GIO开发文件 在构建之前,需要安装GIO的开发包。根据您的Linux发行版,安装命令可能不同: - Debian/Ubuntu: ```bash sudo apt-get install libglib2.0-dev libgio3.0-dev # 或者使用libgio3.0-dev(根据版本) ``` - Fedora/RHEL (参考引用[1]中的安装命令,但注意引用中安装的包很多,我们只需要GIO相关的): ```bash sudo dnf install glib2-devel # 这个包已经包含了GIO ``` ### 步骤2: 在CMakeLists.txt中配置GIO #### 方法1: 使用pkg-config(推荐) GLib和GIO都提供了pkg-config文件,我们可以通过`pkg_check_modules`来查找。 ```cmake cmake_minimum_required(VERSION 3.10) project(my_project) # 查找pkg-config find_package(PkgConfig REQUIRED) # 通过pkg-config查找gio-2.0(GIO的pkg-config名称) pkg_check_modules(GIO REQUIRED gio-2.0) # 添加您的可执行文件 add_executable(my_target main.c) # 包含头文件目录 target_include_directories(my_target PRIVATE ${GIO_INCLUDE_DIRS}) # 链接库 target_link_libraries(my_target ${GIO_LIBRARIES}) ``` #### 方法2: 使用CMake自带的Find模块 CMake有内置的`FindPkgConfig`模块,但更推荐使用上面的方法。另外,CMake 3.1+提供了`FindGLIB`模块,但可能不如pkg-config可靠。 ```cmake find_package(GLIB REQUIRED COMPONENTS GIO) # 注意:这个模块可能不是所有CMake版本都有,且可能不完整 # 如果使用FindGLIB,则可能如下使用: if(GLIB_FOUND) include_directories(${GLIB_INCLUDE_DIRS}) target_link_libraries(my_target ${GLIB_LIBRARIES}) endif() ``` 但请注意,`FindGLIB`模块在较新的CMake版本中已被标记为过时,推荐使用pkg-config。 ### 步骤3: 在代码中包含GIO头文件 在您的C代码中,需要包含GIO头文件: ```c #include <gio/gio.h> ``` ### 步骤4: 编译和链接 运行CMake构建: ```bash mkdir build cd build cmake .. make ``` ### 调试链接问题 如果遇到链接错误(如未定义的引用),请检查: 1. 确保GIO开发包已安装。 2. 使用`pkg-config --libs gio-2.0`检查库是否正确安装(应该在终端输出链接选项,如`-lgio-2.0 -lglib-2.0`等)。 3. 在CMakeLists.txt中,确保`target_link_libraries`包含了`${GIO_LIBRARIES}`,并且这个变量包含所有需要的库。 ### 完整示例 以下是一个完整的CMakeLists.txt示例: ```cmake cmake_minimum_required(VERSION 3.10) project(gio_example) # 查找pkg-config find_package(PkgConfig REQUIRED) pkg_check_modules(GIO REQUIRED gio-2.0) add_executable(example main.c) target_include_directories(example PRIVATE ${GIO_INCLUDE_DIRS}) target_link_libraries(example ${GIO_LIBRARIES}) ``` ### 相关问题 1. 如何在CMake中链接多个pkg-config模块? 2. 如何解决CMake找不到GLib/GIO的问题? 3. 在Windows上如何配置CMake来使用GLib和GIO?</think>### 在CMake中正确链接GIO库的完整指南 #### 1. **安装依赖库** 首先确保系统已安装GIO开发包: ```bash # Debian/Ubuntu sudo apt-get install libglib2.0-dev libgio3.0-dev # Fedora/RHEL (参考引用[1]) sudo dnf install glib2-devel gtk2-devel ``` #### 2. **CMake基础配置** 在`CMakeLists.txt`中添加GIO依赖检测: ```cmake cmake_minimum_required(VERSION 3.10) project(my_project) # 使用pkg-config查找GIO find_package(PkgConfig REQUIRED) pkg_check_modules(GIO REQUIRED gio-2.0) # 关键步骤 # 添加可执行文件 add_executable(my_app main.c) # 包含头文件目录 target_include_directories(my_app PRIVATE ${GIO_INCLUDE_DIRS}) # 链接GIO库 target_link_libraries(my_app ${GIO_LIBRARIES}) ``` #### 3. **验证链接配置** 在代码中使用GIO功能验证: ```c #include <gio/gio.h> // GIO主头文件 int main() { // 测试GIO功能 GDBusConnection *bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); g_print("D-Bus连接状态: %s\n", bus ? "成功" : "失败"); return 0; } ``` #### 4. **高级配置选项** 处理不同版本的兼容性: ```cmake # 检查GIO版本 pkg_check_modules(GIO REQUIRED gio-2.0>=2.64) # 添加编译定义 target_compile_definitions(my_app PRIVATE GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_64 ) ``` #### 5. **静态链接配置** 如果需要静态链接: ```cmake # 查找静态库 pkg_check_modules(GIO_STATIC REQUIRED gio-2.0-static) # 覆盖之前的链接设置 target_link_libraries(my_app ${GIO_STATIC_LIBRARIES}) ``` #### 6. **验证构建** 构建和运行测试: ```bash mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release make -j4 # 运行程序 ./my_app # 预期输出: D-Bus连接状态: 成功 ``` #### 7. **故障排除** 常见问题解决方案: 1. **未找到GIO库**: ```bash pkg-config --modversion gio-2.0 # 验证安装 ``` 输出应为版本号如`2.74.0` 2. **链接错误**: ```cmake # 添加缺失的依赖库 target_link_libraries(my_app ${GIO_LIBRARIES} glib-2.0 gobject-2.0 ) ``` 3. **头文件错误**: ```cmake # 显式包含目录 include_directories(${GIO_INCLUDE_DIRS}) ``` #### 8. **跨平台处理** Windows特殊配置: ```cmake if(WIN32) find_package(GLIB REQUIRED) target_link_libraries(my_app glib-2.0 gio-2.0) endif() ``` > 提示:GIO是GLib的一部分,提供高级I/O功能,包括D-Bus支持[^1]。在CMake中通过`pkg_check_modules`可确保正确找到所有依赖项。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值