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 需要线程默认的 GMainContextGTask 被构造起就至少运行到任务完成并释放其数据。

如果已构建 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() 允许您获取任务的 GCancellableGMainContext 和 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 搬运

GTaskAPI 尝试在几个方面比 GSimpleAsyncResult 更加简单

线程安全性考虑

由于某些 API 设计的不便,存在一个线程安全性问题,GTask 的使用者必须注意。

如果在任务完成之前,主线程丢弃了源对象或任务数据的最后一个引用,则这些对象的终结器将在工作线程上被调用。

如果终结器使用非线程安全的 API,这将会成为问题,并可能导致难以调试的崩溃。可能的解决方案包括:

  • notify::completed 的信号处理器中清除任务数据
  • 在主线程中保持迭代主上下文,并在任务完成时将源对象的引用释放延迟到主上下文中。

祖先

实现

构造函数

g_task_new

创建一个在当前 [线程默认主上下文][g-main-context-push-thread-default] 中调用 callbackGTask,它将作用于 source_object

since: 2.36

函数

g_task_is_valid

检查 result 是否为 GTask,以及 source_object 是否是其源对象(或者 source_objectNULL 并且 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 触发的异步操作的实用函数。将 sourcetaskGMainContext 相关联,使用 task优先级,并将 source 的回调设置为 callback,其中 task 作为回调的 user_data

since: 2.36

g_task_get_cancellable

获取 taskGCancellable

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_name

获取 task 的名称。参见 g_task_set_name()。

自:2.60

g_task_get_priority

获取 task 的优先级。

since: 2.36

g_task_get_return_on_cancel

获取 task 的取消后返回标志。有关更多详细信息,请参见 g_task_set_return_on_cancel()

since: 2.36

g_task_get_source_object

task 获取源对象。类似于 g_async_result_get_source_object(),但不会引用对象。

since: 2.36

g_task_get_source_tag

获取 task 的源标签。参见 g_task_set_source_tag()。

since: 2.36

g_task_get_task_data

获取 tasktask_data

since: 2.36

g_task_had_error

测试 task 是否产生了错误。

since: 2.36

g_task_propagate_boolean

以 <#gboolean> 的形式获取 task 的结果。

since: 2.36

g_task_propagate_int

以整数 <#gssize> 的形式获取 task 的结果。

since: 2.36

g_task_propagate_pointer

以指针的形式获取 task 的结果,并将那个值的所有权转移给调用者。

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 的结果设置为 errortask 假定拥有它)并完成任务(有关确切含义,参见 g_task_return_pointer() 的更多讨论)。

since: 2.36

g_task_return_error_if_cancelled

检查 taskGCancellable 是否已被取消,如果是,则相应地设置 task 的错误并完成任务(有关确切含义,参见 g_task_return_pointer() 的更多讨论)。

since: 2.36

g_task_return_int

task 的结果设置为 result 并完成任务(有关确切含义,参见 g_task_return_pointer() 的更多讨论)。

since: 2.36

g_task_return_new_error

task 的结果设置为从 domaincodeformat 和剩余参数创建的新 GError,并完成任务(有关确切含义,参见 g_task_return_pointer() 的更多讨论)。

since: 2.36

g_task_return_new_error_literal

task 的结果设置为从 domaincodemessage 创建的新 GError 并完成任务。

自:2.80

g_task_return_pointer

task 的结果设置为 result 并完成任务。如果 result 不为 NULL,那么如果调用者不使用 g_task_propagate_pointer() 来获取所有权,则会使用 result_destroy 来释放 result

since: 2.36

g_task_return_prefixed_error

task 的结果设置为 errortask 假定拥有它),消息前缀根据 format 设置,并完成任务(有关确切含义,参见 g_task_return_pointer() 的更多讨论)。

自:2.80

g_task_return_value

task 的结果设置为通过复制设置的 result 并完成任务。

自:2.64

g_task_run_in_thread

在另一个线程中运行 task_func。当 task_func 返回时,taskGAsyncReadyCallback 将在 taskGMainContext 中被调用。

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_name

设置 task 的名称,用于调试和性能分析。默认名称为 NULL

自:2.60

g_task_set_priority

设置 task 的优先级。如果您不调用此方法,则默认为 G_PRIORITY_DEFAULT

since: 2.36

g_task_set_return_on_cancel

设置或清除 task 的取消返回标志。这对通过 g_task_run_in_thread 或 g_task_run_in_thread_sync() 运行的任务而言是有意义的。

since: 2.36

g_task_set_source_tag

设置 task 的源标签。

since: 2.36

g_task_set_static_name

设置 task 的名称,用于调试和性能分析。

since: 2.76

g_task_set_task_data

设置 task 的任务数据(如果任何,释放现有的任务数据)。

since: 2.36

GObject 继承的方法(43)

请参阅 GObject 获取方法列表的全貌。

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

如果 resGSimpleAsyncResult,则此方法与 g_simple_async_result_propagate_error 等效。否则返回 FALSE

since: 2.34

属性

Gio.Task:completed

任务是否已完成,这意味着其回调(如果设置)已被调用。

自:2.44

信号

GObject 继承的信号(1)
GObject::notify

当对象的属性值通过 g_object_set_property()、g_object_set() 等设置时,将发出 notify 信号。

类结构

struct GioTaskClass {
  /* no available fields */
}

没有可用的描述。