|
Packit |
1470ea |
|
|
Packit |
1470ea |
<page xmlns="http://projectmallard.org/1.0/" xmlns:its="http://www.w3.org/2005/11/its" xmlns:xi="http://www.w3.org/2003/XInclude" type="topic" id="threading" xml:lang="pt-BR">
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<info>
|
|
Packit |
1470ea |
<link type="guide" xref="index#specific-how-tos"/>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<credit type="author copyright">
|
|
Packit |
1470ea |
<name>Philip Withnall</name>
|
|
Packit |
1470ea |
<email its:translate="no">philip.withnall@collabora.co.uk</email>
|
|
Packit |
1470ea |
<years>2015</years>
|
|
Packit |
1470ea |
</credit>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<include xmlns="http://www.w3.org/2001/XInclude" href="cc-by-sa-3-0.xml"/>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<desc>Movendo computação da thread principal para threads de trabalho</desc>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<mal:credit xmlns:mal="http://projectmallard.org/1.0/" type="translator copyright">
|
|
Packit |
1470ea |
<mal:name>Rafael Fontenelle</mal:name>
|
|
Packit |
1470ea |
<mal:email>rafaelff@gnome.org</mal:email>
|
|
Packit |
1470ea |
<mal:years>2017</mal:years>
|
|
Packit |
1470ea |
</mal:credit>
|
|
Packit |
1470ea |
</info>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<title>Threading</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<synopsis>
|
|
Packit |
1470ea |
<title>Resumo</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<list>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Do not use threads if at all possible.
|
|
Packit |
1470ea |
(<link xref="#when-to-use-threading"/>)
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
If threads have to be used, use GTask or
|
|
Packit |
1470ea |
GThreadPool and isolate the threaded code as much as
|
|
Packit |
1470ea |
possible.
|
|
Packit |
1470ea |
(<link xref="#using-threading"/>)
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Use g_thread_join() to avoid leaking thread resources if
|
|
Packit |
1470ea |
using GThread manually.
|
|
Packit |
1470ea |
(<link xref="#using-threading"/>)
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
Be careful about the GMainContext which code is executed in
|
|
Packit |
1470ea |
if using threads. Executing code in the wrong context can cause race
|
|
Packit |
1470ea |
conditions, or block the main loop.
|
|
Packit |
1470ea |
(<link xref="#using-threading"/>)
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
</list>
|
|
Packit |
1470ea |
</synopsis>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="when-to-use-threading">
|
|
Packit |
1470ea |
<title>When to Use Threading</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
When writing projects using GLib, the default approach should be to
|
|
Packit |
1470ea |
never use threads. Instead, make proper use of the
|
|
Packit |
1470ea |
<link xref="main-contexts">GLib main context</link> which, through the use
|
|
Packit |
1470ea |
of asynchronous operations,
|
|
Packit |
1470ea |
allows most blocking I/O operations to continue in the background while
|
|
Packit |
1470ea |
the main context continues to process other events. Analysis, review and
|
|
Packit |
1470ea |
debugging of threaded code becomes very hard, very quickly.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Threading should only be necessary when using an external library which
|
|
Packit |
1470ea |
has blocking functions which need to be called from GLib code. If the
|
|
Packit |
1470ea |
library provides a non-blocking alternative, or one which integrates with
|
|
Packit |
1470ea |
a
|
|
Packit |
1470ea |
<link href="http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.html">poll() </link>
|
|
Packit |
1470ea |
loop, that should be used in preference. If the blocking function really
|
|
Packit |
1470ea |
must be used, a thin wrapper should be written for it to convert it to the
|
|
Packit |
1470ea |
normal
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/gio/stable/GAsyncResult.html">GAsyncResult
|
|
Packit |
1470ea |
style</link> of GLib asynchronous function, running the blocking operation
|
|
Packit |
1470ea |
in a worker thread.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<example>
|
|
Packit |
1470ea |
Por exemplo:
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
int
|
|
Packit |
1470ea |
some_blocking_function (void *param1,
|
|
Packit |
1470ea |
void *param2);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Should be wrapped as:
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
void
|
|
Packit |
1470ea |
some_blocking_function_async (void *param1,
|
|
Packit |
1470ea |
void *param2,
|
|
Packit |
1470ea |
GCancellable *cancellable,
|
|
Packit |
1470ea |
GAsyncReadyCallback callback,
|
|
Packit |
1470ea |
gpointer user_data);
|
|
Packit |
1470ea |
int
|
|
Packit |
1470ea |
some_blocking_function_finish (GAsyncResult *result,
|
|
Packit |
1470ea |
GError **error);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
With an implementation something like:
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Closure for the call’s parameters. */
|
|
Packit |
1470ea |
typedef struct {
|
|
Packit |
1470ea |
void *param1;
|
|
Packit |
1470ea |
void *param2;
|
|
Packit |
1470ea |
} SomeBlockingFunctionData;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
static void
|
|
Packit |
1470ea |
some_blocking_function_data_free (SomeBlockingFunctionData *data)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
free_param (data->param1);
|
|
Packit |
1470ea |
free_param (data->param2);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_free (data);
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
static void
|
|
Packit |
1470ea |
some_blocking_function_thread_cb (GTask *task,
|
|
Packit |
1470ea |
gpointer source_object,
|
|
Packit |
1470ea |
gpointer task_data,
|
|
Packit |
1470ea |
GCancellable *cancellable)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
SomeBlockingFunctionData *data = task_data;
|
|
Packit |
1470ea |
int retval;
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Handle cancellation. */
|
|
Packit |
1470ea |
if (g_task_return_error_if_cancelled (task))
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
return;
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Run the blocking function. */
|
|
Packit |
1470ea |
retval = some_blocking_function (data->param1, data->param2);
|
|
Packit |
1470ea |
g_task_return_int (task, retval);
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
void
|
|
Packit |
1470ea |
some_blocking_function_async (void *param1,
|
|
Packit |
1470ea |
void *param2,
|
|
Packit |
1470ea |
GCancellable *cancellable,
|
|
Packit |
1470ea |
GAsyncReadyCallback callback,
|
|
Packit |
1470ea |
gpointer user_data)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
GTask *task = NULL; /* owned */
|
|
Packit |
1470ea |
SomeBlockingFunctionData *data = NULL; /* owned */
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_return_if_fail (validate_param (param1));
|
|
Packit |
1470ea |
g_return_if_fail (validate_param (param2));
|
|
Packit |
1470ea |
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
task = g_task_new (NULL, cancellable, callback, user_data);
|
|
Packit |
1470ea |
g_task_set_source_tag (task, some_blocking_function_async);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Cancellation should be handled manually using mechanisms specific to
|
|
Packit |
1470ea |
* some_blocking_function(). */
|
|
Packit |
1470ea |
g_task_set_return_on_cancel (task, FALSE);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Set up a closure containing the call’s parameters. Copy them to avoid
|
|
Packit |
1470ea |
* locking issues between the calling thread and the worker thread. */
|
|
Packit |
1470ea |
data = g_new0 (SomeBlockingFunctionData, 1);
|
|
Packit |
1470ea |
data->param1 = copy_param (param1);
|
|
Packit |
1470ea |
data->param2 = copy_param (param2);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_task_set_task_data (task, data, some_blocking_function_data_free);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
/* Run the task in a worker thread and return immediately while that continues
|
|
Packit |
1470ea |
* in the background. When it’s done it will call @callback in the current
|
|
Packit |
1470ea |
* thread default main context. */
|
|
Packit |
1470ea |
g_task_run_in_thread (task, some_blocking_function_thread_cb);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
g_object_unref (task);
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
int
|
|
Packit |
1470ea |
some_blocking_function_finish (GAsyncResult *result,
|
|
Packit |
1470ea |
GError **error)
|
|
Packit |
1470ea |
{
|
|
Packit |
1470ea |
g_return_val_if_fail (g_task_is_valid (result,
|
|
Packit |
1470ea |
some_blocking_function_async), -1);
|
|
Packit |
1470ea |
g_return_val_if_fail (error == NULL || *error == NULL, -1);
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
return g_task_propagate_int (G_TASK (result), error);
|
|
Packit |
1470ea |
}
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
See the
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/gio/stable/GAsyncResult.html">GAsyncResult
|
|
Packit |
1470ea |
documentation</link> for more details. A simple way to implement the
|
|
Packit |
1470ea |
worker thread is to use
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/gio/stable/GTask.html">GTask </link>
|
|
Packit |
1470ea |
and <link href="https://developer.gnome.org/gio/stable/GTask.html#g-task-run-in-thread">g_task_run_in_thread() </link>.
|
|
Packit |
1470ea |
(See also: <link xref="main-contexts#gtask"/>.)
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</example>
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="using-threading">
|
|
Packit |
1470ea |
<title>Using Threading</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
If GTask is not suitable for writing the worker thread, a
|
|
Packit |
1470ea |
more low-level approach must be used. This should be considered very
|
|
Packit |
1470ea |
carefully, as it is very easy to get threading code wrong in ways which
|
|
Packit |
1470ea |
will unpredictably cause bugs at runtime, cause deadlocks, or consume too
|
|
Packit |
1470ea |
many resources and terminate the program.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
A full manual on writing threaded code is beyond the scope of this
|
|
Packit |
1470ea |
document, but here are a number of guidelines to follow which should
|
|
Packit |
1470ea |
reduce the potential for bugs in threading code. The overriding principle
|
|
Packit |
1470ea |
is to reduce the amount of code and data which can be affected by
|
|
Packit |
1470ea |
threading — for example, reducing the number of threads, the complexity of
|
|
Packit |
1470ea |
worker thread implementation, and the amount of data shared between
|
|
Packit |
1470ea |
threads.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<list>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Use <link href="https://developer.gnome.org/glib/stable/glib-Thread-Pools.html">GThreadPool </link>
|
|
Packit |
1470ea |
instead of manually creating
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-Threads.html">GThread s</link>
|
|
Packit |
1470ea |
if possible. GThreadPool supports a work queue, limits on
|
|
Packit |
1470ea |
the number of spawned threads, and automatically joins finished
|
|
Packit |
1470ea |
threads so they are not leaked.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
If it is not possible to use a GThreadPool (which is
|
|
Packit |
1470ea |
rarely the case):
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<list>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Use <link href="https://developer.gnome.org/glib/stable/glib-Threads.html#g-thread-try-new">g_thread_try_new() </link>
|
|
Packit |
1470ea |
to spawn threads, instead of
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-Threads.html#g-thread-new">g_thread_new() </link>,
|
|
Packit |
1470ea |
so errors due to the system running out of threads can be handled
|
|
Packit |
1470ea |
gracefully rather than unconditionally aborting the program.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Explicitly join threads using
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-Threads.html#g-thread-join">g_thread_join() </link>
|
|
Packit |
1470ea |
to avoid leaking the thread resources.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
</list>
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Use message passing to transfer data between threads, rather than
|
|
Packit |
1470ea |
manual locking with mutexes. GThreadPool explicitly
|
|
Packit |
1470ea |
supports this with
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-Thread-Pools.html#g-thread-pool-push">g_thread_pool_push() </link>.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
If mutexes must be used:
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<list>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Isolate threading code as much as possible, keeping mutexes
|
|
Packit |
1470ea |
private within classes, and tightly bound to very specific class
|
|
Packit |
1470ea |
members.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
All mutexes should be clearly commented beside their declaration,
|
|
Packit |
1470ea |
indicating which other structures or variables they protect access
|
|
Packit |
1470ea |
to. Similarly, those variables should be commented saying that
|
|
Packit |
1470ea |
they should only be accessed with that mutex held.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
</list>
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
<item>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Be careful about interactions between main contexts and threads. For
|
|
Packit |
1470ea |
example,
|
|
Packit |
1470ea |
<link href="https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-timeout-add-seconds">g_timeout_add_seconds() </link>
|
|
Packit |
1470ea |
adds a timeout to be executed in the global default main
|
|
Packit |
1470ea |
context, which is being run in the main thread, not
|
|
Packit |
1470ea |
necessarily the current thread. Getting this wrong can mean that
|
|
Packit |
1470ea |
work intended for a worker thread accidentally ends up being executed
|
|
Packit |
1470ea |
in the main thread anyway. (See also:
|
|
Packit |
1470ea |
<link xref="main-contexts#default-contexts"/>.)
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</item>
|
|
Packit |
1470ea |
</list>
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
<section id="debugging">
|
|
Packit |
1470ea |
<title>Depuração</title>
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
Debugging threading issues is tricky, both because they are hard to
|
|
Packit |
1470ea |
reproduce, and because they are hard to reason about. This is one of the
|
|
Packit |
1470ea |
big reasons for avoiding using threads in the first place.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
However, if a threading issue does arise,
|
|
Packit |
1470ea |
<link xref="tooling#helgrind-and-drd">Valgrind’s drd and helgrind tools
|
|
Packit |
1470ea |
are useful</link>.
|
|
Packit |
1470ea |
|
|
Packit |
1470ea |
</section>
|
|
Packit |
1470ea |
</page>
|