类
GioTask
描述 [源代码]
final class Gio.Task : GObject.Object
implements Gio.AsyncResult {
/* No available fields */
}
GTask 代表并管理一个可取消的“任务”。
异步操作
GTask 最常见的用法是作为 GAsyncResult 使用,用于在异步操作期间管理数据。在 ‘start’ 方法中调用 g_task_new(),如果需要将一些附加数据与任务相关联,然后调用 g_task_set_task_data() 等方法,再通过异步操作传递任务对象。最后,将调用类似于 g_task_return_pointer() 或 g_task_return_error() 的方法,它将保存您给出的值,并在创建任务的线程默认主上下文中调用任务的回调函数(见 g_main_context_push_thread_default()),在创建处调用(如果有必要,等待主循环下一次迭代)。调用者将 GTask 返回给操作的完成函数(作为 GAsyncResult),您可以使用 g_task_propagate_pointer() 等方法提取返回值。
使用 GTask 需要线程默认的 GMainContext 从 GTask 被构造起就至少运行到任务完成并释放其数据。
如果已构建 GTask 并设置了回调,未在它上面调用 g_task_return_*() 是错误的。GLib 从 2.76 版本起将在运行时警告这种情况(如果发生)。
以下是一个将 GTask 作为 GAsyncResult 使用的示例
typedef struct {
CakeFrostingType frosting;
char *message;
} DecorationData;
static void
decoration_data_free (DecorationData *decoration)
{
g_free (decoration->message);
g_slice_free (DecorationData, decoration);
}
static void
baked_cb (Cake *cake,
gpointer user_data)
{
GTask *task = user_data;
DecorationData *decoration = g_task_get_task_data (task);
GError *error = NULL;
if (cake == NULL)
{
g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_NO_FLOUR,
"Go to the supermarket");
g_object_unref (task);
return;
}
if (!cake_decorate (cake, decoration->frosting, decoration->message, &error))
{
g_object_unref (cake);
// g_task_return_error() takes ownership of error
g_task_return_error (task, error);
g_object_unref (task);
return;
}
g_task_return_pointer (task, cake, g_object_unref);
g_object_unref (task);
}
void
baker_bake_cake_async (Baker *self,
guint radius,
CakeFlavor flavor,
CakeFrostingType frosting,
const char *message,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
DecorationData *decoration;
Cake *cake;
task = g_task_new (self, cancellable, callback, user_data);
if (radius < 3)
{
g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_TOO_SMALL,
"%ucm radius cakes are silly",
radius);
g_object_unref (task);
return;
}
cake = _baker_get_cached_cake (self, radius, flavor, frosting, message);
if (cake != NULL)
{
// _baker_get_cached_cake() returns a reffed cake
g_task_return_pointer (task, cake, g_object_unref);
g_object_unref (task);
return;
}
decoration = g_slice_new (DecorationData);
decoration->frosting = frosting;
decoration->message = g_strdup (message);
g_task_set_task_data (task, decoration, (GDestroyNotify) decoration_data_free);
_baker_begin_cake (self, radius, flavor, cancellable, baked_cb, task);
}
Cake *
baker_bake_cake_finish (Baker *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (result, self), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
链式异步操作
GTask 还试图简化内部级联多个较小异步操作的异步操作。在启动新子任务时,g_task_get_cancellable()、g_task_get_context() 和 g_task_get_priority() 允许您获取任务的 GCancellable、GMainContext 和 I/O 优先级,这样您就无需自己跟踪它们。 g_task_attach_source() 简化了等待源被触发的情况(自动使用正确的 GMainContext 和优先级)。
以下是一个链式异步操作的示例
typedef struct {
Cake *cake;
CakeFrostingType frosting;
char *message;
} BakingData;
static void
decoration_data_free (BakingData *bd)
{
if (bd->cake)
g_object_unref (bd->cake);
g_free (bd->message);
g_slice_free (BakingData, bd);
}
static void
decorated_cb (Cake *cake,
GAsyncResult *result,
gpointer user_data)
{
GTask *task = user_data;
GError *error = NULL;
if (!cake_decorate_finish (cake, result, &error))
{
g_object_unref (cake);
g_task_return_error (task, error);
g_object_unref (task);
return;
}
// baking_data_free() will drop its ref on the cake, so we have to
// take another here to give to the caller.
g_task_return_pointer (task, g_object_ref (cake), g_object_unref);
g_object_unref (task);
}
static gboolean
decorator_ready (gpointer user_data)
{
GTask *task = user_data;
BakingData *bd = g_task_get_task_data (task);
cake_decorate_async (bd->cake, bd->frosting, bd->message,
g_task_get_cancellable (task),
decorated_cb, task);
return G_SOURCE_REMOVE;
}
static void
baked_cb (Cake *cake,
gpointer user_data)
{
GTask *task = user_data;
BakingData *bd = g_task_get_task_data (task);
GError *error = NULL;
if (cake == NULL)
{
g_task_return_new_error (task, BAKER_ERROR, BAKER_ERROR_NO_FLOUR,
"Go to the supermarket");
g_object_unref (task);
return;
}
bd->cake = cake;
// Bail out now if the user has already cancelled
if (g_task_return_error_if_cancelled (task))
{
g_object_unref (task);
return;
}
if (cake_decorator_available (cake))
decorator_ready (task);
else
{
GSource *source;
source = cake_decorator_wait_source_new (cake);
// Attach @source to @task’s GMainContext and have it call
// decorator_ready() when it is ready.
g_task_attach_source (task, source, decorator_ready);
g_source_unref (source);
}
}
void
baker_bake_cake_async (Baker *self,
guint radius,
CakeFlavor flavor,
CakeFrostingType frosting,
const char *message,
gint priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
BakingData *bd;
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_priority (task, priority);
bd = g_slice_new0 (BakingData);
bd->frosting = frosting;
bd->message = g_strdup (message);
g_task_set_task_data (task, bd, (GDestroyNotify) baking_data_free);
_baker_begin_cake (self, radius, flavor, cancellable, baked_cb, task);
}
Cake *
baker_bake_cake_finish (Baker *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (result, self), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
从同步操作到异步操作
您可以使用 g_task_run_in_thread() 将同步操作转换为异步操作,通过在线程中运行它。当完成时,结果将发送到创建 GTask 的线程默认主上下文(见 g_main_context_push_thread_default())中。
在线程中运行任务
typedef struct {
guint radius;
CakeFlavor flavor;
CakeFrostingType frosting;
char *message;
} CakeData;
static void
cake_data_free (CakeData *cake_data)
{
g_free (cake_data->message);
g_slice_free (CakeData, cake_data);
}
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;
cake = bake_cake (baker, 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);
}
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;
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);
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);
}
Cake *
baker_bake_cake_finish (Baker *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (result, self), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
向不可取消任务添加可取消性
最后,g_task_run_in_thread() 和 g_task_run_in_thread_sync() 可以用于将不可取消操作转换为可取消操作。如果您调用 g_task_set_return_on_cancel() 并传递 TRUE,则如果取消任务的 GCancellable,它将立即将控制权返回给调用者,同时允许任务线程在后台继续运行(并在最终完成时简单地丢弃其结果)。只要任务线程小心使用锁和其他外部可见资源,这允许您创建 'GLib 友好' 的异步和可取消的同步阻塞 API 变体。
取消任务
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;
cake = bake_cake (baker, cake_data->radius, cake_data->flavor,
cake_data->frosting, cake_data->message,
&error);
if (error)
{
g_task_return_error (task, error);
return;
}
// If the task has already been cancelled, then we don’t want to add
// the cake to the cake cache. Likewise, we don’t want to have the
// task get cancelled in the middle of updating the cache.
// g_task_set_return_on_cancel() will return %TRUE here if it managed
// to disable return-on-cancel, or %FALSE if the task was cancelled
// before it could.
if (g_task_set_return_on_cancel (task, FALSE))
{
// If the caller cancels at this point, their
// GAsyncReadyCallback won’t be invoked until we return,
// so we don’t have to worry that this code will run at
// the same time as that code does. But if there were
// other functions that might look at the cake cache,
// then we’d probably need a GMutex here as well.
baker_add_cake_to_cache (baker, cake);
g_task_return_pointer (task, cake, g_object_unref);
}
}
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;
cake_data = g_slice_new (CakeData);
...
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free);
g_task_set_return_on_cancel (task, TRUE);
g_task_run_in_thread (task, bake_cake_thread);
}
Cake *
baker_bake_cake_sync (Baker *self,
guint radius,
CakeFlavor flavor,
CakeFrostingType frosting,
const char *message,
GCancellable *cancellable,
GError **error)
{
CakeData *cake_data;
GTask *task;
Cake *cake;
cake_data = g_slice_new (CakeData);
...
task = g_task_new (self, cancellable, NULL, NULL);
g_task_set_task_data (task, cake_data, (GDestroyNotify) cake_data_free);
g_task_set_return_on_cancel (task, TRUE);
g_task_run_in_thread_sync (task, bake_cake_thread);
cake = g_task_propagate_pointer (task, error);
g_object_unref (task);
return cake;
}
从 GSimpleAsyncResult 搬运
GTask 的 API 尝试在几个方面比 GSimpleAsyncResult 更加简单
- 您可以使用
g_task_set_task_data()保存特定任务的数据,并使用g_task_get_task_data()在以后检索它。这取代了之前为了相同目的滥用g_simple_async_result_set_op_res_gpointer()以及GSimpleAsyncResult - GTask 也会跟踪与任务关联的 优先级、
GCancellable以及GMainContext,因此由一系列简单的异步操作组成的任务在启动每个子任务时可以轻松访问这些值。 g_task_return_error_if_cancelled()为取消操作提供了简化的处理方法。另外,默认情况下,取消操作会覆盖GTask的任何其他返回值,就像当调用g_simple_async_result_set_check_cancellable()时GSimpleAsyncResult所做的。 (您可以使用g_task_set_check_cancellable()来禁用此行为。) 另一方面,g_task_run_in_thread()保证始终运行您的task_func,即使任务的GCancellable在任务有机会运行之前就已经被取消;如果您需要旧的行为,可以在启动task_func时使用g_task_return_error_if_cancelled()检查。- ‘return’ 方法(例如,
g_task_return_pointer())会自动使任务完成,无需担心“完成”与“空闲时完成”的区别。 (GTask会自动确定任务的回调是否可以直接调用,是否需要发送到另一个GMainContext,或延迟到当前GMainContext的下一个迭代。) - 基于
GTask的操作的“finish”函数通常比GSimpleAsyncResult简单得多,通常只包含对g_task_propagate_pointer()或类似的单个调用。由于g_task_propagate_pointer()“偷取”了GTask的返回值,因此不需要操纵指针以防止它们被释放两次。 - 使用
GSimpleAsyncResult,通常在_finish()封装函数中调用g_simple_async_result_propagate_error(),并且虚拟方法实现只处理成功返回。这种行为已被弃用,因为它使得子类很难串联到父类的异步方法。相反,封装函数应该只是一个简单的封装,并且虚拟方法应该调用适当的g_task_propagate_函数。注意,封装方法现在可以使用g_async_result_legacy_propagate_error()来实现旧的GSimpleAsyncResult错误返回行为,并使用g_async_result_is_tagged()来检查结果是否标记为来自_async()封装函数(例如,当将0传递给g_input_stream_read_async()时)。
线程安全性考虑
由于某些 API 设计的不便,存在一个线程安全性问题,GTask 的使用者必须注意。
如果在任务完成之前,主线程丢弃了源对象或任务数据的最后一个引用,则这些对象的终结器将在工作线程上被调用。
如果终结器使用非线程安全的 API,这将会成为问题,并可能导致难以调试的崩溃。可能的解决方案包括:
- 在
notify::completed的信号处理器中清除任务数据 - 在主线程中保持迭代主上下文,并在任务完成时将源对象的引用释放延迟到主上下文中。
构造函数
g_task_new
创建一个在当前 [线程默认主上下文][g-main-context-push-thread-default] 中调用 callback 的 GTask,它将作用于 source_object。
since: 2.36
函数
g_task_is_valid
检查 result 是否为 GTask,以及 source_object 是否是其源对象(或者 source_object 是 NULL 并且 result 没有源对象)。这可以在 g_return_if_fail() 检查中使用。
since: 2.36
g_task_report_error
创建一个 GTask 并立即在它上面调用 g_task_return_error()。在异步方法的封装函数中,当想要避免甚至调用虚拟方法时,可以使用此方法。然后,可以在完成方法封装中使用 g_async_result_is_tagged() 来检查结果是否标记为由封装方法创建的,如果标记,则适当处理。
since: 2.36
g_task_report_new_error
创建一个 GTask 并立即在其中调用 g_task_return_new_error()。在异步方法的封装函数中,当想要避免甚至调用虚拟方法时,可以使用此方法。然后,可以在完成方法封装中使用 g_async_result_is_tagged() 来检查结果是否标记为由封装方法创建的,如果标记,则适当处理。
since: 2.36
实例方法
g_task_attach_source
用于处理需要等待 GSource 触发的异步操作的实用函数。将 source 与 task‘ 的 GMainContext 相关联,使用 task‘ 的 优先级,并将 source‘ 的回调设置为 callback,其中 task 作为回调的 user_data。
since: 2.36
g_task_get_check_cancellable
获取 task‘ 的检查可取消标志。有关更多详细信息,请参阅 g_task_set_check_cancellable()。
since: 2.36
g_task_get_completed
获取 GTask:completed 的值。在任务的回调被调用后,此值从 FALSE 变为 TRUE,如果在回调内部调用它,将返回 FALSE。
自:2.44
g_task_get_context
获取 GMainContext,该上下文是任务返回结果的地方(即,在创建 task 的时刻,是 [线程默认主上下文][g-main-context-push-thread-default] 的上下文)。
since: 2.36
g_task_get_return_on_cancel
获取 task 的取消后返回标志。有关更多详细信息,请参见 g_task_set_return_on_cancel()。
since: 2.36
g_task_propagate_value
以 GValue 的形式获取 task 的结果,并将该值的所有权转移给调用者。与 g_task_return_value() 类似,这是一个通用的低级方法;通常,对于 C 代码来说,g_task_propagate_pointer() 等方法会更有用。
自:2.64
g_task_return_boolean
将 task 的结果设置为 result 并完成任务(有关确切含义,参见 g_task_return_pointer() 的更多讨论)。
since: 2.36
g_task_return_error
将 task 的结果设置为 error(task 假定拥有它)并完成任务(有关确切含义,参见 g_task_return_pointer() 的更多讨论)。
since: 2.36
g_task_return_error_if_cancelled
检查 task 的 GCancellable 是否已被取消,如果是,则相应地设置 task 的错误并完成任务(有关确切含义,参见 g_task_return_pointer() 的更多讨论)。
since: 2.36
g_task_return_new_error
将 task 的结果设置为从 domain、code、format 和剩余参数创建的新 GError,并完成任务(有关确切含义,参见 g_task_return_pointer() 的更多讨论)。
since: 2.36
g_task_return_pointer
将 task 的结果设置为 result 并完成任务。如果 result 不为 NULL,那么如果调用者不使用 g_task_propagate_pointer() 来获取所有权,则会使用 result_destroy 来释放 result。
since: 2.36
g_task_return_prefixed_error
将 task 的结果设置为 error(task 假定拥有它),消息前缀根据 format 设置,并完成任务(有关确切含义,参见 g_task_return_pointer() 的更多讨论)。
自:2.80
g_task_run_in_thread
在另一个线程中运行 task_func。当 task_func 返回时,task‘ 的 GAsyncReadyCallback 将在 task‘ 的 GMainContext 中被调用。
since: 2.36
g_task_run_in_thread_sync
在另一个线程中运行 task_func,并等待其返回或被取消。之后,您可以使用 g_task_propagate_pointer() 等,以获取 task_func 的结果。
since: 2.36
g_task_set_check_cancellable
设置或清除 task‘ 的检查可取消标志。如果是 TRUE(默认值),那么 g_task_propagate_pointer、等,以及 g_task_had_error 将首先检查任务的 GCancellable 是否已取消,如果已经取消,则它们将认为任务返回了一个“操作已取消”错误(G_IO_ERROR_CANCELLED),无论任务是否还有其他错误或返回值。
since: 2.36
g_task_set_return_on_cancel
设置或清除 task‘ 的取消返回标志。这对通过 g_task_run_in_thread 或 g_task_run_in_thread_sync() 运行的任务而言是有意义的。
since: 2.36
从 GAsyncResult 继承的方法(4)
g_async_result_get_source_object
从 GAsyncResult 中获取源对象。
g_async_result_get_user_data
从 GAsyncResult 中获取用户数据。
g_async_result_is_tagged
检查 res 是否具有给定的 source_tag(通常是一个函数指针,指示 res 被创建的函数)。
since: 2.34
g_async_result_legacy_propagate_error
如果 res 是 GSimpleAsyncResult,则此方法与 g_simple_async_result_propagate_error 等效。否则返回 FALSE。
since: 2.34
信号
从 GObject 继承的信号(1)
GObject::notify
当对象的属性值通过 g_object_set_property()、g_object_set() 等设置时,将发出 notify 信号。