Threading¶
Summary¶
Do not use threads if at all possible.
If threads have to be used, use
GTaskorGThreadPooland isolate the threaded code as much as possible.Use
g_thread_join()to avoid leaking thread resources if usingGThreadmanually.Be careful about the
GMainContextwhich code is executed in if using threads. Executing code in the wrong context can cause race conditions, or block the main loop.
When to use threading¶
When writing projects using GLib, the default approach should be to never use threads. Instead, make proper use of the GLib main context which, through the use of asynchronous operations, allows most blocking I/O operations to continue in the background while the main context continues to process other events. Analysis, review and debugging of threaded code becomes very hard, very quickly.
Threading should only be necessary when using an external library which has
blocking functions which need to be called from GLib code. If the library
provides a non-blocking alternative, or one which integrates with a poll()
loop, that should be used in preference. If the blocking function really must be
used, a thin wrapper should be written for it to convert it to the normal
GAsyncResult style of GLib asynchronous function, running the blocking
operation in a worker thread.
For example, the following blocking function:
int
some_blocking_function (void *param1,
void *param2);
Should be wrapped by this pair of functions:
void
some_blocking_function_async (void *param1,
void *param2,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
int
some_blocking_function_finish (GAsyncResult *result,
GError **error);
With an implementation like:
/* Closure for the call’s parameters. */
typedef struct {
void *param1;
void *param2;
} SomeBlockingFunctionData;
static void
some_blocking_function_data_free (SomeBlockingFunctionData *data)
{
free_param (data->param1);
free_param (data->param2);
g_free (data);
}
static void
some_blocking_function_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
SomeBlockingFunctionData *data = task_data;
int retval;
/* Handle cancellation. */
if (g_task_return_error_if_cancelled (task))
{
return;
}
/* Run the blocking function. */
retval = some_blocking_function (data->param1, data->param2);
g_task_return_int (task, retval);
}
void
some_blocking_function_async (void *param1,
void *param2,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task = NULL; /* owned */
SomeBlockingFunctionData *data = NULL; /* owned */
g_return_if_fail (validate_param (param1));
g_return_if_fail (validate_param (param2));
g_return_if_fail (cancellable == NULL