Потоки

Threads — Абстракция потока; включает потоки, различные взаимоисключения (mutexes), условия и поток закрытых данных.

Краткое описание

#include <glib.h> #define G_THREADS_ENABLED #define G_THREADS_IMPL_POSIX #define G_THREADS_IMPL_NONE #define G_THREAD_ERROR enum GThreadError; GThreadFunctions; void g_thread_init (GThreadFunctions *vtable); gboolean g_thread_supported (); gpointer (*GThreadFunc) (gpointer data); enum GThreadPriority; GThread; GThread* g_thread_create (GThreadFunc func, gpointer data, gboolean joinable, GError **error); GThread* g_thread_create_full (GThreadFunc func, gpointer data, gulong stack_size, gboolean joinable, gboolean bound, GThreadPriority priority, GError **error); GThread* g_thread_self (void); gpointer g_thread_join (GThread *thread); void g_thread_set_priority (GThread *thread, GThreadPriority priority); void g_thread_yield (); void g_thread_exit (gpointer retval); void g_thread_foreach (GFunc thread_func, gpointer user_data); GMutex; GMutex* g_mutex_new (); void g_mutex_lock (GMutex *mutex); gboolean g_mutex_trylock (GMutex *mutex); void g_mutex_unlock (GMutex *mutex); void g_mutex_free (GMutex *mutex); GStaticMutex; #define G_STATIC_MUTEX_INIT void g_static_mutex_init (GStaticMutex *mutex); void g_static_mutex_lock (GStaticMutex *mutex); gboolean g_static_mutex_trylock (GStaticMutex *mutex); void g_static_mutex_unlock (GStaticMutex *mutex); GMutex* g_static_mutex_get_mutex (GStaticMutex *mutex); void g_static_mutex_free (GStaticMutex *mutex); #define G_LOCK_DEFINE (name) #define G_LOCK_DEFINE_STATIC (name) #define G_LOCK_EXTERN (name) #define G_LOCK (name) #define G_TRYLOCK (name) #define G_UNLOCK (name) GStaticRecMutex; #define G_STATIC_REC_MUTEX_INIT void g_static_rec_mutex_init (GStaticRecMutex *mutex); void g_static_rec_mutex_lock (GStaticRecMutex *mutex); gboolean g_static_rec_mutex_trylock (GStaticRecMutex *mutex); void g_static_rec_mutex_unlock (GStaticRecMutex *mutex); void g_static_rec_mutex_lock_full (GStaticRecMutex *mutex, guint depth); guint g_static_rec_mutex_unlock_full (GStaticRecMutex *mutex); void g_static_rec_mutex_free (GStaticRecMutex *mutex); GStaticRWLock; #define G_STATIC_RW_LOCK_INIT void g_static_rw_lock_init (GStaticRWLock *lock); void g_static_rw_lock_reader_lock (GStaticRWLock *lock); gboolean g_static_rw_lock_reader_trylock (GStaticRWLock *lock); void g_static_rw_lock_reader_unlock (GStaticRWLock *lock); void g_static_rw_lock_writer_lock (GStaticRWLock *lock); gboolean g_static_rw_lock_writer_trylock (GStaticRWLock *lock); void g_static_rw_lock_writer_unlock (GStaticRWLock *lock); void g_static_rw_lock_free (GStaticRWLock *lock); GCond; GCond* g_cond_new (); void g_cond_signal (GCond *cond); void g_cond_broadcast (GCond *cond); void g_cond_wait (GCond *cond, GMutex *mutex); gboolean g_cond_timed_wait (GCond *cond, GMutex *mutex, GTimeVal *abs_time); void g_cond_free (GCond *cond); GPrivate; GPrivate* g_private_new (GDestroyNotify destructor); gpointer g_private_get (GPrivate *private_key); void g_private_set (GPrivate *private_key, gpointer data); GStaticPrivate; #define G_STATIC_PRIVATE_INIT void g_static_private_init (GStaticPrivate *private_key); gpointer g_static_private_get (GStaticPrivate *private_key); void g_static_private_set (GStaticPrivate *private_key, gpointer data, GDestroyNotify notify); void g_static_private_free (GStaticPrivate *private_key); GOnce; enum GOnceStatus; #define G_ONCE_INIT #define g_once (once, func, arg)

Описание

Потоки действуют почти как процессы, но в отличии от процессов все потоки одного процесса совместно используют одну и туже память. Это хорошо, поскольку обеспечивает простую взаимосвязь между вовлечёнными потоками через общую память, и это плохо, потому что могут происходить странные вещи (так называемый "Heisenbugs") если программа спроектирована не достаточно внимательно. В частности из-за параллельной природы потоков, выполняемый код не может выполняться в разных потоках, если программист явно не назначил это через синхронизацию примитивов.

Цель связанных с потоком функций в GLib заключается в обеспечении переносимого способа создания многопоточных программ. Есть примитивы для взаимоисключений для защиты доступа к участкам памяти (GMutex, GStaticMutex, G_LOCK_DEFINE, GStaticRecMutex и GStaticRWLock). Есть примитивы для переменных условий позволяющие синхронизацию потоков (GCond). Есть примитивы для закрытых потоковых данных - данных которые каждый поток имеет закрытый экземпляр (GPrivate, GStaticPrivate). И не в последнюю очередь есть примитивы для портативного создания и управления потоками (GThread).

Вы должны вызывать g_thread_init() перед выполнением любых других функций GLib в потоковых GLib программах. После этого, GLib полностью потоко-безопасен (все глобальные данные автоматически блокируются), но индивидуальные экземпляры структур данных не блокируются автоматически по причине выполнения. Поэтому, например вы должны координировать доступ к одной и той же GHashTable из множества потоков. Известно два исключения из этого правила GMainLoop и GAsyncQueue, которые являются потоко-безопасными и не нуждаются в дальнейшей блокировки на уровне приложения для доступности из множества потоков.

Детали

G_THREADS_ENABLED

#define G_THREADS_ENABLED

Этот макрос определяется если GLib была скомпилирована с поддержкой потоков. Это не означает обязательную доступность реализации потоков, но это значит что необходимая инфраструктура на месте и как только вы обеспечите реализацию потоков в g_thread_init(), GLib станет потоко-безопасной. Если G_THREADS_ENABLED не определён, то Glib не может быть безопасной для много-поточности.


G_THREADS_IMPL_POSIX

#define G_THREADS_IMPL_POSIX

Этот макрос определяется если используется стиль потоков POSIX.


G_THREADS_IMPL_NONE

#define G_THREADS_IMPL_NONE

Этот макрос определяется если нет используемой реализации потоков. Вы можете однако обеспечить это с помощью g_thread_init() для создания безопасной много-поточности GLib.


G_THREAD_ERROR

#define G_THREAD_ERROR g_thread_error_quark ()

Область ошибок в подсистеме потоков GLib.


enum GThreadError

typedef enum { G_THREAD_ERROR_AGAIN /* Ресурс временно не доступен */ } GThreadError;

Возможные ошибки связанные с функциями потока.

G_THREAD_ERROR_AGAIN поток не создан из-за нехватки ресурсов. Попробуйте повторить попытку позже.

GThreadFunctions

typedef struct { GMutex* (*mutex_new) (void); void (*mutex_lock) (GMutex *mutex); gboolean (*mutex_trylock) (GMutex *mutex); void (*mutex_unlock) (GMutex *mutex); void (*mutex_free) (GMutex *mutex); GCond* (*cond_new) (void); void (*cond_signal) (GCond *cond); void (*cond_broadcast) (GCond *cond); void (*cond_wait) (GCond *cond, GMutex *mutex); gboolean (*cond_timed_wait) (GCond *cond, GMutex *mutex, GTimeVal *end_time); void (*cond_free) (GCond *cond); GPrivate* (*private_new) (GDestroyNotify destructor); gpointer (*private_get) (GPrivate *private_key); void (*private_set) (GPrivate *private_key, gpointer data); void (*thread_create) (GThreadFunc func, gpointer data, gulong stack_size, gboolean joinable, gboolean bound, GThreadPriority priority, gpointer thread, GError **error); void (*thread_yield) (void); void (*thread_join) (gpointer thread); void (*thread_exit) (void); void (*thread_set_priority)(gpointer thread, GThreadPriority priority); void (*thread_self) (gpointer thread); gboolean (*thread_equal) (gpointer thread1, gpointer thread2); } GThreadFunctions;

Эта таблица функций используется g_thread_init() для инициализации системы потоков. Функции в таблице непосредственно используются их копиями с предустановленным в названиях g_* (описаны в данном документе). Например, если вы вызвали g_mutex_new(), тогда будет вызвана mutex_new() из таблицы g_thread_init().

Примечание

Не используйте эту структуры если не уверены в том что делаете.


g_thread_init ()

void g_thread_init (GThreadFunctions *vtable);

Если вы используете GLib из более одного потока, вы должны инициализировать систему потоков вызовом g_thread_init(). В основном вам нужно вызывать g_thread_init (NULL).

Примечание

Не вызывайте g_thread_init() с не-NULL параметром если не уверены в том что делаете.

Примечание

g_thread_init() не должна вызываться непосредственно или косвенно как callback из GLib. Также взаимоисключения не могут блокироваться в течении вызова g_thread_init().

g_thread_init() можно вызвать только один раз. Второй вызов вернёт ошибку. Если вы хотите убедиться в том что система потоков инициализирована, вы можете сделать следующее:

if (!g_thread_supported ()) g_thread_init (NULL);

После этой команды либо инициализируется система потоков либо, ели нет доступной системы потоков в GLib (то есть либо G_THREADS_ENABLED не определён, либо определён G_THREADS_IMPL_NONE), программа будет отменена.

Если нет доступной системы потоков и параметр vtable равен NULL, или если не все элементы vtable являются не-NULL, то g_thread_init() будет отменён.

Примечание

Для использования g_thread_init() в вашей программе, вы должны привязаться к библиотекам которые выдаёт команда pkg-config --libs gthread-2.0. Это не касается остальных связанных с потоками функций GLib. Они могут использоваться без привязки к потоковым библиотекам.

vtable : таблица функций типа GThreadFunctions, которые обеспечивают точки входа в используемую систему потоков.

g_thread_supported ()

gboolean g_thread_supported ();

Эта функция возвращает TRUE если система потоков инициализирована, а FALSE если нет.

Примечание

Эта функция является фактически макрокомандой. Однако, кроме захвата адреса её можно использовать как функуию.

Возвращает : TRUE, если система потоков инициализирована.

GThreadFunc ()

gpointer (*GThreadFunc) (gpointer data);

Определяет тип функций func помещаемых в g_thread_create() или g_thread_create_full().

data : данные помещаемые в поток.
Возвращает : значение возвращаемое потоком, которое будет возвращено g_thread_join().

enum GThreadPriority

typedef enum { G_THREAD_PRIORITY_LOW, G_THREAD_PRIORITY_NORMAL, G_THREAD_PRIORITY_HIGH, G_THREAD_PRIORITY_URGENT } GThreadPriority;

Определяет приоритет потока.

Примечание

Не гарантируется что потоки с разными приоритетами будут вести себя соответственно. В некоторых системах (например Linux) нет приоритетов потоков. В других системах (например Solaris) существует разное планирование для разных приоритетов. В основном пытайтесь избегать зависимости от приоритетов.

G_THREAD_PRIORITY_LOW приоритет ниже нормального
G_THREAD_PRIORITY_NORMAL приоритет по умолчанию
G_THREAD_PRIORITY_HIGH приоритет выше нормального
G_THREAD_PRIORITY_URGENT наивысший приоритет

GThread

typedef struct { } GThread;

GThread структура представляет выполняемый поток. Она имеет три общих элемента доступных только для чтения, но основных в структуре больше, поэтому вы не должны копировать эту структуру.

Примечание

Ресурсы для объединяемого потока реализуются не полностью пока для этого потока вызвана g_thread_join().


g_thread_create ()

GThread* g_thread_create (GThreadFunc func, gpointer data, gboolean joinable, GError **error);

Эта функция создаёт новый поток с приоритетом по умолчанию.

Если joinable равен TRUE, вы можете подождать завершения потоков вызывающих g_thread_join(). Иначе поток просто исчезнет когда она завершиться.

Новый поток выполняет функцию func с параметром data. Если поток был создан успешно, он возвращается.

error может быть NULL для игнорирования ошибок, или не-NULL для сообщения об ошибках. Ошибка устанавливается только если функция возвращает NULL.

func : функция выполняемая в новом потоке.
data : аргумент поставляемый новому потоку.
joinable : должен ли поток быть объединяемым?
error : размещение для возвращаемой ошибки.
Возвращает : новый GThread при успешном выполнении.

g_thread_create_full ()

GThread* g_thread_create_full (GThreadFunc func, gpointer data, gulong stack_size, gboolean joinable, gboolean bound, GThreadPriority priority, GError **error);

Эта функция создаёт поток с приоритетом priority. Если реализация потока поддерживает это, поток получает размер стека stack_size или значение по умолчанию для текущей платформы, если stack_size это 0.

Если joinable равно TRUE, вы можете подождать завершения вызова g_thread_join() для потока. Иначе поток просто исчезнет при завершении. Если bound равен TRUE, этот поток будет планироваться в системной области, иначе реализация это свободно выполняемое планирование в пределах процесса. Первый вариант более требователен к ресурсам, но в общем быстрее. В некоторых системах (например Linux) все потоки связаны.

Новый поток выполняет функцию func с аргументом data. Если поток был успешно создан, он возвращается.

error может быть NULL для игнорирования ошибок, или не-NULL для сообщения об ошибках. Ошибка устанавливается, только если функция вернула NULL.

Примечание

Не гарантируется что потоки с разными приоритетами будут вести себя соответственно. В некоторых системах (например Linux) нет приоритетов потоков. В других системах (например Solaris) существует разное планирование для разных приоритетов. В основном пытайтесь избегать зависимости от приоритетов. Используйте G_THREAD_PRIORITY_NORMAL здесь как значение по умолчанию.

Примечание

Используйте g_thread_create_full() только если вы действительно не можете использовать g_thread_create() вместо неё. g_thread_create() не применяет stack_size, bound, и priority как аргументы, поскольку они должны использоваться только если это явно не избежно.

func : функция для выполнения в новом потоке.
data : аргумент поставляемый в новый поток.
stack_size : размер стека для нового потока.
joinable : должен ли этот поток быть объединяемым?
bound : должен ли этот поток привязываться к системе потоков?
priority : приоритет потока.
error : расположение для возвращаемой ошибки.
Возвращает : новый GThread при удачном выполнении.

g_thread_self ()

GThread* g_thread_self (void);

Эта функция возвращает GThread соответствующий вызываемому потоку.

Возвращает : текущий поток.

g_thread_join ()

gpointer g_thread_join (GThread *thread);

Ждёт завершения thread, то есть функции func, полученной в g_thread_create(), возврат или g_thread_exit() вызванный thread. Все ресурсы thread включая структуру GThread освобождаются. thread должен быть создан с joinable=TRUE в g_thread_create(). Значение возвращаемое func или полученное в g_thread_exit()thread возвращается этой функцией.

thread : GThread из которого ожидается результат.
Возвращает : возвращаемое потоком значение.

g_thread_set_priority ()

void g_thread_set_priority (GThread *thread, GThreadPriority priority);

Изменяет приоритет thread в значение priority.

Примечание

Не гарантируется что потоки с разными приоритетами будут вести себя соответственно. В некоторых системах (например Linux) нет приоритетов потоков. В других системах (например Solaris) существует разное планирование для разных приоритетов. В основном пытайтесь избегать зависимости от приоритетов.

thread : GThread.
priority : новый приоритет для thread.

g_thread_yield ()

void g_thread_yield ();

Позволяет запланировать другой поток.

Эта функция часто используется как метод для создания занятого ожидания менее вредным. Но в большинстве случаев есть лучше методы для этого. Поэтому в основном вы не должны использовать эту функцию.


g_thread_exit ()

void g_thread_exit (gpointer retval);

Выход из текущего потока. Если другой поток ожидает завершение этого потока используя функцию g_thread_join() и текущий поток является совмещённым, то ожидающий поток будет разбужен и получит retval как возвращаемое значение g_thread_join(). Если текущий поток не совмещён, retval игнорируется. Вызов

g_thread_exit (retval);

эквивалентен вызову

return retval;

в функции func, полученной в g_thread_create().

Примечание

Никогда не вызывайте g_thread_exit() внутри потока GThreadPool, так как это запутает расчеты и приведёт к нежелательным последствиям.

retval : значение возвращаемое этим потоком.

g_thread_foreach ()

void g_thread_foreach (GFunc thread_func, gpointer user_data);

Вызывает thread_func на всех существующих структурах GThread. Помните, что потоки могут принять решение о выходе в то время как thread_func выполняется, поэтому без близкого знания продолжительности жизни внешних потоков, thread_func не имеет доступа к указателю GThread* помещаемому в первый аргумент. Однако, thread_func не будет вызвана для потоков выход из которых уже совершён.

Из-за проверок жизненного цикла потока, эта функция имеет сложности при выполнении которые взводятся в степень количества существующих потоков.

thread_func : функция вызываемая для всех структур GThread
user_data : второй аргумент для thread_func

Начиная с версии 2.10


GMutex

typedef struct _GMutex GMutex;

Структура GMutex - закрытая структура данных представляющая взаимоисключения (mutex - mutual exclusion). Она может использоваться для защиты данных от совместного доступа. Возьмём например следующую функцию:

Пример 3. Функция которая не будет работать в потоковом окружении

int give_me_next_number () { static int current_number = 0; /* теперь выполняем очень cложное вычисление для расчёта нового числа, это может быть например генератор случайных чисел */ current_number = calc_next_number (current_number); return current_number; }

Просто заметить что это не будет работать в многопоточных приложениях. Переменная current_number должна быть защищена от совместного доступа. Первая наивная реализация могла бы быть такой:

Пример 4. Не правильный способ создания потоко-безопасной функции

int give_me_next_number () { static int current_number = 0; int ret_val; static GMutex * mutex = NULL; if (!mutex) mutex = g_mutex_new (); g_mutex_lock (mutex); ret_val = current_number = calc_next_number (current_number); g_mutex_unlock (mutex); return ret_val; }

Похоже что это должно работать, но есть условие гонки пока создаётся взаимоисключение и этот код не может надежно работать. Пожалуйста не используйте эту конструкцию в собственных программах! Вот одно работающее решение:

Пример 5. A correct thread-safe function

static GMutex *give_me_next_number_mutex = NULL; /* эта функция должна быть вызвана перед любым вызовом give_me_next_number () она должна вызываться только один раз. */ void init_give_me_next_number () { g_assert (give_me_next_number_mutex == NULL); give_me_next_number_mutex = g_mutex_new (); } int give_me_next_number () { static int current_number = 0; int ret_val; g_mutex_lock (give_me_next_number_mutex); ret_val = current_number = calc_next_number (current_number); g_mutex_unlock (give_me_next_number_mutex); return ret_val; }

GStaticMutex обеспечивает более простой и безопасный способ выполнения этого.

Если вы хотите использовать взаимоисключения и ваш код должен также работать не вызывая сначала g_thread_init(), то вы не можете использовать GMutex, так как g_mutex_new() требует инициализации системы потоков. Вместо этого используйте GStaticMutex.

Доступ к GMutex должен производиться только с помощью функций описанных ниже.

Примечание

Все g_mutex_* функции фактически являются макрокомандами. Кроме взятия адреса, вы можете однако использовать их как будто они являются функциями.


g_mutex_new ()

GMutex* g_mutex_new ();

Создаёт новую структуру GMutex.

Примечание

Эта функция прерывается если не вызвана g_thread_init().

Возвращает : новая структура GMutex.

g_mutex_lock ()

void g_mutex_lock (GMutex *mutex);

Блокирует mutex. Если mutex уже блокирован другим потоком, то текущий поток блокируется пока mutex не будет разблокирован.

Эта функция может использоваться даже если g_thread_init() не была вызвана, в этом случае она не делает ничего.

Примечание

GMutex не гарантирует не рекурсию не её отсутсвие, то есть поток может зависнуть вызывая g_mutex_lock(), если она уже заблокировала mutex. Используйте GStaticRecMutex, если вам нужны рекурсивные взаимоисключения.

mutex : GMutex.

g_mutex_trylock ()

gboolean g_mutex_trylock (GMutex *mutex);

Пытается блокировать mutex. Если mutex уже заблокирован другим потоком, она немедленно возвращает FALSE. Иначе блокируется mutex и возвращается TRUE.

Эта функция может использоваться даже если g_thread_init() не была вызвана, в этом случае она немедленно вернёт TRUE.

Примечание

GMutex не гарантирует рекурсию не её отсутсвие, то есть возвращаемое значение g_mutex_trylock() может быть и FALSE и TRUE, если текущий поток уже заблокировал mutex. Используйте GStaticRecMutex, если вам нужны рекурсивные взаимоисключения.

mutex : GMutex.
Возвращает : TRUE, если mutex может быть заблокирован.

g_mutex_unlock ()

void g_mutex_unlock (GMutex *mutex);

Разблокирует mutex. Если другой поток блокирован в g_mutex_lock() вызовом для mutex, то он будет разбужен и сможет заблокировать mutex самостоятельно.

Эта функция может использоваться даже если g_thread_init() не была вызвана, в этом случае она ничего не делает.

mutex : GMutex.

g_mutex_free ()

void g_mutex_free (GMutex *mutex);

Уничтожает mutex.

mutex : GMutex.

GStaticMutex

typedef struct _GStaticMutex GStaticMutex;

GStaticMutex работает также как GMutex, но имеет одно существенное преимущество. Она не должна создаваться во время выполнения как GMutex, а может быть определена во время компиляции. Вот более короткий и более безопасный пример нашей функции give_me_next_number():

Пример 6. Использование GStaticMutex для упрощения потокобезопасного программирования

int give_me_next_number () { static int current_number = 0; int ret_val; static GStaticMutex mutex = G_STATIC_MUTEX_INIT; g_static_mutex_lock (&mutex); ret_val = current_number = calc_next_number (current_number); g_static_mutex_unlock (&mutex); return ret_val; }

Иногда вам может понадобиться создавать динамические взаимоисключения. Если вы не хотите предварительно вызывать g_thread_init(), потому что ваш код должен также использоваться в не потоковых программах, вы не сможете использовать g_mutex_new() а также GMutex, так как требуется предварительный вызов g_thread_init(). В этом случае вы также можете использовать GStaticMutex. Она должна быть инициализирована перед использованием с помощью g_static_mutex_init() и освобождена с помощью g_static_mutex_free() когда нет больше никаких освобождаемых распределённых ресурсов.

Даже при том, что GStaticMutex не непрозрачная структура, она должна использоваться только следующими функциями, так как она определяется по разному на разных платформах.

Все функции g_static_mutex_* могут использоваться даже если не была вызвана g_thread_init().

Примечание

Все функции g_static_mutex_* фактически являются макросами. Однако, кроме взятия адреса, вы можете использовать их как функции.


G_STATIC_MUTEX_INIT

#define G_STATIC_MUTEX_INIT

С помощью этого макроса должна инициализироваться GStaticMutex, перед использованием. Этот макрос может использоваться для инициализации переменной, но он не может присваиваться переменной. В этом случае вы должны использовать g_static_mutex_init().

GStaticMutex my_mutex = G_STATIC_MUTEX_INIT;


g_static_mutex_init ()

void g_static_mutex_init (GStaticMutex *mutex);

Инициализирует mutex. Альтернативно его можно инициализировать с помощью G_STATIC_MUTEX_INIT.

mutex : GStaticMutex для инициализации.

g_static_mutex_lock ()

void g_static_mutex_lock (GStaticMutex *mutex);

Работает как g_mutex_lock(), но для GStaticMutex.

mutex : GStaticMutex.

g_static_mutex_trylock ()

gboolean g_static_mutex_trylock (GStaticMutex *mutex);

Работает как g_mutex_trylock(), но для GStaticMutex.

mutex : GStaticMutex.
Возвращает : TRUE, если GStaticMutex может быть заблокирован.

g_static_mutex_unlock ()

void g_static_mutex_unlock (GStaticMutex *mutex);

Работает как g_mutex_unlock(), но для GStaticMutex.

mutex : GStaticMutex.

g_static_mutex_get_mutex ()

GMutex* g_static_mutex_get_mutex (GStaticMutex *mutex);

Для некоторых операций (как g_cond_wait()) вы должны использовать GMutex вместо GStaticMutex. Эта функция соответственно возвращает GMutex для mutex.

mutex : GStaticMutex.
Возвращает : GMutex соответствующий mutex.

g_static_mutex_free ()

void g_static_mutex_free (GStaticMutex *mutex);

Освобождает все ресурсы распределённые для mutex.

Вы не должны вызывать эту функцию для GStaticMutex с неограниченным жизненным циклом, то есть для объектов объявленных как 'static', но если GStaticMutex является членом структуры и структура освобождается, вы должны также освободить GStaticMutex.

mutex : GStaticMutex для освобождения.

G_LOCK_DEFINE()

#define G_LOCK_DEFINE(name)

Макрос G_LOCK_* обеспечивает удобный интерфейс для GStaticMutex с преимуществом распространения для программ скомпилированных без поддержки потоков GLib, сохраняя код и память. G_LOCK_DEFINE определяет блокировку. Он может появляться там же где могут появляться переменные в программах, то есть в первом блоке функции или вне функций. Параметр name будет изменён для получения имени GStaticMutex. Это значит что вы можете использовать имена существующих переменных как параметр - например имя переменной вы можете защитить с помощью блокировки. Рассмотрим наш пример give_me_next_number() с использованием G_LOCK_* macros:

Пример 7. Использование удобного макроса G_LOCK_*

G_LOCK_DEFINE (current_number); int give_me_next_number () { static int current_number = 0; int ret_val; G_LOCK (current_number); ret_val = current_number = calc_next_number (current_number); G_UNLOCK (current_number); return ret_val; }

name : имя блокировки.

G_LOCK_DEFINE_STATIC()

#define G_LOCK_DEFINE_STATIC(name)

Работает также как G_LOCK_DEFINE, но создаёт статический объект.

name : имя блокировки.

G_LOCK_EXTERN()

#define G_LOCK_EXTERN(name)

Объявляет блокировку, которая определена с помощью G_LOCK_DEFINE в другом модуле.

name : имя блокировки.

G_LOCK()

#define G_LOCK(name)

Работает также как g_mutex_lock(), но для блокировки определённой с помощьюG_LOCK_DEFINE.

name : имя блокировки.

G_TRYLOCK()

#define G_TRYLOCK(name)

Работает также как g_mutex_trylock(), но для блокировки определённой с помощью G_LOCK_DEFINE.

name : имя блокировки.
Возвращает : TRUE, если блокировка может быть блокирована.

G_UNLOCK()

#define G_UNLOCK(name)

Работает также как g_mutex_unlock(), но для блокировки определённой с помощью G_LOCK_DEFINE.

name : имя блокировки.

GStaticRecMutex

typedef struct { } GStaticRecMutex;

GStaticRecMutex работает также как GStaticMutex, но он может быть блокирован множество раз одним потоком. Если вы ввели его n раз, вы должны разблокировать его n раз позволяя другим потокам блокировать его. Исключением является функция g_static_rec_mutex_unlock_full(): она позволяет вам разблокировать GStaticRecMutex на всю глубину полностью, (то есть на то количество раз на которое был заблокирован этот объект mutex). Глубина может позже использоваться для восстановления состояния GStaticRecMutex с помощью вызова g_static_rec_mutex_lock_full().

Даже при том, что GStaticRecMutex не является закрытой, она должна использоваться только следующими функциями.

Все функции g_static_rec_mutex_* могут использоваться даже если не была вызвана g_thread_init().


G_STATIC_REC_MUTEX_INIT

#define G_STATIC_REC_MUTEX_INIT { G_STATIC_MUTEX_INIT }

GStaticRecMutex должна быть инициализирована с помощью макроса перед использованием. Этот макрос может использоваться для инициализации переменных, но он не должен назначаться переменным. Для этого случая используется g_static_rec_mutex_init().

GStaticRecMutex my_mutex = G_STATIC_REC_MUTEX_INIT;


g_static_rec_mutex_init ()

void g_static_rec_mutex_init (GStaticRecMutex *mutex);

GStaticRecMutex должна инициализироваться с помощью этой функции перед использованием. Альтернативно вы можете инициализировать её с помощью G_STATIC_REC_MUTEX_INIT.

mutex : GStaticRecMutex для инициализации.

g_static_rec_mutex_lock ()

void g_static_rec_mutex_lock (GStaticRecMutex *mutex);

Блокирует mutex. Если mutex уже заблокирован другим потоком, текущий поток будет заблокирован пока mutex не разблокируется. Если mutex уже заблокирован вызываемым потоком, эта функция увеличит глубину блокировки mutex и немедленно возвратит.

mutex : GStaticRecMutex для блокирования.

g_static_rec_mutex_trylock ()

gboolean g_static_rec_mutex_trylock (GStaticRecMutex *mutex);

Пытается блокировать mutex. Если mutex уже заблокирован другим потоком, она немедленно возвращает FALSE. Иначе mutex блокируется и возвращается TRUE. Если mutex уже заблокирован вызываемым потоком, эта функция увеличивает глубину блокировки mutex и немедленно возвращает TRUE.

mutex : GStaticRecMutex для блокировки.
Возвращает : TRUE, если mutex может быть заблокирован.

g_static_rec_mutex_unlock ()

void g_static_rec_mutex_unlock (GStaticRecMutex *mutex);

Разблокирует mutex. Другой поток может быть допущен к блокировки mutex только когда он будет разблокирован такое же количество раз сколько раз был заблокирован перед этим. Если mutex полностью разблокирован а другой поток блокирован в g_static_rec_mutex_lock() вызове для mutex, он будет разбужен и сможет заблокировать mutex самостоятельно.

mutex : GStaticRecMutex для разблокирования.

g_static_rec_mutex_lock_full ()

void g_static_rec_mutex_lock_full (GStaticRecMutex *mutex, guint depth);

Работает также как вызов g_static_rec_mutex_lock() для mutex depth раз.

mutex : GStaticRecMutex для разблокирования.
depth : количество разблокирований для полного разблокирования.

g_static_rec_mutex_unlock_full ()

guint g_static_rec_mutex_unlock_full (GStaticRecMutex *mutex);

Полностью разблокирует mutex. Если другой поток блокирован в g_static_rec_mutex_lock() вызове для mutex, он будет разбужен и сможет заблокировать mutex самостоятельно. Эта функция возвращает количество раз которое mutex был заблокирован текущим потоком. Для восстановления состояния перед вызовом g_static_rec_mutex_unlock_full() вы можете вызвать g_static_rec_mutex_lock_full() с глубиной возвращаемой этой функцией.

mutex : GStaticRecMutex для полного разблокирования.
Возвращает : количество раз которым mutex был заблокирован текущим процессом.

g_static_rec_mutex_free ()

void g_static_rec_mutex_free (GStaticRecMutex *mutex);

Освобождает все ресурсы распределённые для GStaticRecMutex.

Вы не должны вызывать эту функцию для GStaticRecMutex с безграничным жизненным циклом, то есть для объектов объявленных как 'static', но если GStaticRecMutex является членом освобождаемой структуры, вы должны также освободить GStaticRecMutex.

mutex : GStaticRecMutex для освобождения.

GStaticRWLock

typedef struct { } GStaticRWLock;

Структура GStaticRWLock представляет блокировку чтения-записи. Блокировка чтения-записи может использоваться для защиты данных которые некоторые части кода только читают, в то время как другие также записывают. В таких ситуациях конечно лучше чтобы читать могли несколько процессов/потоков одновременно, а писать только один. Рассмотрим следующий пример:

Пример 8. Массив с функциями доступа

GStaticRWLock rwlock = G_STATIC_RW_LOCK_INIT; GPtrArray *array; gpointer my_array_get (guint index) { gpointer retval = NULL; if (!array) return NULL; g_static_rw_lock_reader_lock (&rwlock); if (index < array->len) retval = g_ptr_array_index (array, index); g_static_rw_lock_reader_unlock (&rwlock); return retval; } void my_array_set (guint index, gpointer data) { g_static_rw_lock_writer_lock (&rwlock); if (!array) array = g_ptr_array_new (); if (index >= array->len) g_ptr_array_set_size (array, index+1); g_ptr_array_index (array, index) = data; g_static_rw_lock_writer_unlock (&rwlock); }

Этот пример демонстрирует массив доступ к которому позволен многим "читателям" (my_array_get() функция) одновременно, тогда как "писателям" (my_array_set() функция) он доступен только одному за раз и только если нет "читателей" обращающихся к массиву в текущий момент. Это из-за потенциальной опасности изменения размера массива. При таком использовании функции полностью безопасны в много-поточности.

В большинстве случаев, "писатель" должен иметь преимущество перед "читателями". Это значит, для этой реализации, как только "писатель" захочет блокировать данные никому из "читателей" не позволено блокировать их, хотя "читателям" которые уже блокировали данные конечно позволено завершить свои операции. Как только последний "читатель" разблокировал данные, "писатель" блокирует их.

Даже при том, что структура GStaticRWLock не является закрытой, доступ к ней должен осуществляться только с помощью функций описанных ниже.

Все функции g_static_rw_lock_* могут использоваться даже если g_thread_init() не была вызвана.

Примечание

Блокировка чтение-запись имеет приоритет над взаимоисключениями (mutex). Например, обе g_static_rw_lock_reader_lock() и g_static_rw_lock_reader_unlock() должны блокировать и разблокировать GStaticMutex, поэтому по крайней мере дважды блокируется и разблокируется GStaticRWLock который в свою очередь блокирует и разблокирует GStaticMutex. Таким образом структуры данных которые доступны для множества "читателей" и которые сохраняют блокировку в течении длительного времени оправдывают GStaticRWLock. Выше упомянутый пример вероятней всего намного лучше использовать с GStaticMutex.


G_STATIC_RW_LOCK_INIT

#define G_STATIC_RW_LOCK_INIT { G_STATIC_MUTEX_INIT, NULL, NULL, 0, FALSE, 0, 0 }

GStaticRWLock должна быть инициализирована макросом перед использованием. Этот макрос может использоваться для инициализации переменных, но он не должен присваиваться переменным. В этом случае вы должны использовать g_static_rw_lock_init().

GStaticRWLock my_lock = G_STATIC_RW_LOCK_INIT;


g_static_rw_lock_init ()

void g_static_rw_lock_init (GStaticRWLock *lock);

GStaticRWLock должна быть инициализирована с помощью этой функции перед использованием. Альтернативно вы можете инициализировать её с помощью G_STATIC_RW_LOCK_INIT.

lock : GStaticRWLock для инициализации.

g_static_rw_lock_reader_lock ()

void g_static_rw_lock_reader_lock (GStaticRWLock *lock);

Блокирует lock для чтения. Могут быть бесконечные параллельные блокировки для одновременного чтения GStaticRWLock. Если lock уже заблокирован для записи другим потоком или если другой поток уже ожидает блокировки lock для записи, эта функция будет блокирована пока lock не разблокирует другой поток записи и никакие другие потоки не ожидают блокировки lock. Эта блокировка должна разблокироваться с помощью g_static_rw_lock_reader_unlock().

GStaticRWLock не имеет рекурсии. Это может показаться возможным для рекурсивной блокировки чтения, но может привести к зависанию из-за привилегий "писателя".

lock : GStaticRWLock для блокировки чтения.

g_static_rw_lock_reader_trylock ()

gboolean g_static_rw_lock_reader_trylock (GStaticRWLock *lock);

Пытается блокировать lock для чтения. Если lock уже заблокирован для записи другим потоком или другой поток уже ожидает блокировку lock для записи, немедленно возвращает FALSE. Иначе блокирует lock для чтения и возвращает TRUE. Эта блокировка должна разблокироваться с помощью g_static_rw_lock_reader_unlock().

lock : GStaticRWLock для блокирования чтения.
Возвращает : TRUE, если lock можно блокировать для чтения.

g_static_rw_lock_reader_unlock ()

void g_static_rw_lock_reader_unlock (GStaticRWLock *lock);

Разблокирует lock. Если поток ожидает блокировку lock для записи и все блокировки для чтения были разблокированы, ожидающий поток пробуждается и может заблокировать lock для записи.

lock : GStaticRWLock для разблокирования после чтения.

g_static_rw_lock_writer_lock ()

void g_static_rw_lock_writer_lock (GStaticRWLock *lock);

Блокирует lock для записи. Если lock уже заблокирован для записи или чтения другим потоком, эта функция блокируется до тех пор пока lock полностью не разблокируется а затем блокирует lock для записи. Пока эта функция ожидает блокировки lock, другие потоки не могут блокировать lock для чтения. Когда lock заблокирован для записи, другие потоки не могут блокировать lock (не для чтения не для записи). Эта блокировка должна разблокироваться с помощью g_static_rw_lock_writer_unlock().

lock : GStaticRWLock блокируемая для записи.

g_static_rw_lock_writer_trylock ()

gboolean g_static_rw_lock_writer_trylock (GStaticRWLock *lock);

Пытается блокировать lock для записи. Если lock уже заблокирован (для записи или чтенияg) другим потоком, она немедленно возвращает FALSE. Иначе блокирует lock для записи и возвращает TRUE. Эта блокировка должна быть разблокирована с помощью g_static_rw_lock_writer_unlock().

lock : GStaticRWLock блокируемая для записи.
Возвращает : TRUE, если lock может блокироваться для записи.

g_static_rw_lock_writer_unlock ()

void g_static_rw_lock_writer_unlock (GStaticRWLock *lock);

Разблокирует lock. Если поток ожидает блокировки lock для записи и все блокировки для чтения разблокированы, ожидающий поток просыпается и может блокировать lock для записи. Если не тпотоков ожидающих блокировки lock для записи, а некоторые потоки ожидают блокировку lock для чтения, ожидающие потоки просыпаются и могут блокировать lock для чтения.

lock : GStaticRWLock для разблокирования после записи.

g_static_rw_lock_free ()

void g_static_rw_lock_free (GStaticRWLock *lock);

Освобождает все ресурсы распределённые для lock.

Вы не должны вызывать эту функцию для GStaticRWLock с безграничным жизненным циклом, то есть для объектов объявленных статическими 'static', но если GStaticRWLock является частью структуры и структура освобождается, то вы также должны освободить GStaticRWLock.

lock : GStaticRWLock для освобождения.

GCond

typedef struct _GCond GCond;

Структура GCond является закрытой структурой данных представляющих условие. Потоки могут блокироваться на GCond если они обнаружат определённое ложное условие. Если другие потоки изменяют состояние этого условия они сообщают GCond и тогда ожидающий поток просыпается.

Пример 9. Использование GCond для блокировки потока пока условие не удавлетворено

GCond* data_cond = NULL; /* Должен быть где-нибудь инициализирован */ GMutex* data_mutex = NULL; /* Должен быть где-нибудь инициализирован */ gpointer current_data = NULL; void push_data (gpointer data) { g_mutex_lock (data_mutex); current_data = data; g_cond_signal (data_cond); g_mutex_unlock (data_mutex); } gpointer pop_data () { gpointer data; g_mutex_lock (data_mutex); while (!current_data) g_cond_wait (data_cond, data_mutex); data = current_data; current_data = NULL; g_mutex_unlock (data_mutex); return data; }

Теперь, каждый раз когда поток вызывает pop_data(), он будет ждать пока current_data будет не-NULL, то есть пока какой-нибудь поток вызовет push_data().

Примечание

Важно использовать g_cond_wait() и g_cond_timed_wait() функции только внутри цикла который проверяет правильность условия. Не гарантируется что ожидающий поток будет искать условие выполнения после пробуждения, даже если сообщающий поток сбросит условие в это состояние: другой поток может переключить условие прежде чем ожидающий поток проснётся, даже если само условие защищено с помощью GMutex, как описано выше.

GCond доступна только через функции описанные далее.

Примечание

Все g_cond_* функции фактически являются макросами. Однако, за исключением взятия адреса, вы можете использовать их как функции.


g_cond_new ()

GCond* g_cond_new ();

Создаёт новую GCond. Эта функция прерывается, если не была вызвана g_thread_init().

Возвращает : новая GCond.

g_cond_signal ()

void g_cond_signal (GCond *cond);

Если потоки ожидают условие cond, только один из них пробуждается. Хорошая практика удерживать блокировку ожидающего потока пока вызывается эта функция, хотя и необязательно.

Эта функция может использоваться даже если g_thread_init() не была вызвана, в этом случае она ничего не делает.

cond : GCond.

g_cond_broadcast ()

void g_cond_broadcast (GCond *cond);

Если потоки ожидают условие cond, все они пробуждаются. Хорошей практикой является блокирование ожидающих потоков некоторым взаимоисключением (mutex), пока вызывается эта функция, хотя не обязательно.

Эта функция может использоваться даже если g_thread_init() не была вызвана, в этом случае она ничего не делает.

cond : GCond.

g_cond_wait ()

void g_cond_wait (GCond *cond, GMutex *mutex);

Заставляет поток ждать условие cond. Взаимоисключение mutex разблокируется перед отключением и блокируется после возобнавления.

Эта функция может использоваться даже если g_thread_init() не была вызвана, в этом случае она немедленно возвращает результат.

cond : GCond.
mutex : GMutex, который в настоящее время заблокирован.

g_cond_timed_wait ()

gboolean g_cond_timed_wait (GCond *cond, GMutex *mutex, GTimeVal *abs_time);

Заставляет поток ждать условия пробуждения cond, но не дольше чем определено параметром abs_time. Взаимоисключение mutex разблокируется перед отключением и блокируется снова после возобнавления.

Если abs_time равен NULL, g_cond_timed_wait() действует как g_cond_wait().

Эта функция может использоваться даже если g_thread_init() не была вызвана, в этом случае она немедленно возвращает TRUE.

Для простоты вычисления abs_time может использоваться комбинация g_get_current_time() и g_time_val_add().

cond : GCond.
mutex : GMutex который заблокирован в текущий момент.
abs_time : GTimeVal, определяющая максимальное время ожидания.
Возвращает : TRUE если об условии cond было сообщено, или FALSE при истечении времени ожидания.

g_cond_free ()

void g_cond_free (GCond *cond);

Уничтожает GCond.

cond : GCond.

GPrivate

typedef struct _GPrivate GPrivate;

Структура GPrivate является закрытой структурой данных представляющая индивидуальные ключевые данные для потоков. Потоки таким образом могут устанавливать и получать указатели которые индивидуальны для текущего потока. Возьмём наш выше приведенный пример give_me_next_number(). Предположим что мы не хотим чтобы переменная current_number распределялясь между потоками, а вместо этого была бы индивидуальной для каждого потока. Это можно сделать следующим образом:

Пример 10. Using GPrivate for per-thread data

GPrivate* current_number_key = NULL; /* Должна быть где-нибудь инициализирована */ /* с помощью g_private_new (g_free); */ int give_me_next_number () { int *current_number = g_private_get (current_number_key); if (!current_number) { current_number = g_new (int,1); *current_number = 0; g_private_set (current_number_key, current_number); } *current_number = calc_next_number (*current_number); return *current_number; }

Здесь указатель принадлежащий ключу current_number_key читается. Если он равен NULL, то не устанавливается. Затем получаем память для целочисленного значения, привязываем эту память к указателю и записываем указатель обратно. Теперь мы имеем целочисленное значение которое индивидуально для текущего потока.

К структуре GPrivate должны обращаться только следующие функции.

Примечание

Все функции g_private_* фактически являются макросами. Однако, за исключением взятия адреса, вы можете использовать их как обычные функции.


g_private_new ()

GPrivate* g_private_new (GDestroyNotify destructor);

Создаёт новую GPrivate. Если destructor не-NULL, то указывает на разрушающую функцию. Каждый раз когда поток завершается и ключевой указатель соответствующий этому образцу GPrivate не является NULL, разрушающая функция вызывается с этим указателем в качестве параметра.

Примечание

destructor используется иначе чем notify в g_static_private_set().

Примечание

GPrivate не может освобождаться. Вместо этого используйте её повторно, или используйте GStaticPrivate.

Примечание

Эта функция прерывается если g_thread_init() не была вызвана.

destructor : функция для уничтожения ключевых данных в GPrivate когда поток завершён.
Возвращает : новая GPrivate.

g_private_get ()

gpointer g_private_get (GPrivate *private_key);

Возвращает ключевой указатель private_key для текущего потока. Если g_private_set() не была вызвана для текущего private_key и потока, этот указатель будет NULL.

Эта функция может использоваться даже если g_thread_init() не была вызвана, в этом случае она возвращает значение private_key приведённый к gpointer.

private_key : GPrivate.
Возвращает : соответсвующий указатель.

g_private_set ()

void g_private_set (GPrivate *private_key, gpointer data);

Устанавливает ключевой указатель private_key для текущего потока.

Эта функция может использоваться даже если g_thread_init() не была вызвана, в этом случае будет установлен private_key в data приведенный к GPrivate*.

private_key : GPrivate.
data : новый указатель.

GStaticPrivate

typedef struct { } GStaticPrivate;

GStaticPrivate работает также как GPrivate, но имеет одно существенное приимущество. Она не должна создаваться во время выполнения как GPrivate, а может быть определена во время компиляции. Это подобно различию между GMutex и GStaticMutex. Теперь взглянем на наш пример give_me_next_number() с использованием GStaticPrivate:

Пример 11. Использование GStaticPrivate в качестве потоковых данных

int give_me_next_number () { static GStaticPrivate current_number_key = G_STATIC_PRIVATE_INIT; int *current_number = g_static_private_get (&current_number_key); if (!current_number) { current_number = g_new (int,1); *current_number = 0; g_static_private_set (&current_number_key, current_number, g_free); } *current_number = calc_next_number (*current_number); return *current_number; }


G_STATIC_PRIVATE_INIT

#define G_STATIC_PRIVATE_INIT

Все GStaticPrivate перед использованием должны быть инициализированы этим макросом.

GStaticPrivate my_private = G_STATIC_PRIVATE_INIT;


g_static_private_init ()

void g_static_private_init (GStaticPrivate *private_key);

Инициализирует private_key. Альтернативно вы можете инициализировать его с помощью G_STATIC_PRIVATE_INIT.

private_key : GStaticPrivate для инициализации.

g_static_private_get ()

gpointer g_static_private_get (GStaticPrivate *private_key);

Работает также как g_private_get() только для GStaticPrivate.

Эта функция работает даже если g_thread_init() не была вызвана.

private_key : GStaticPrivate.
Возвращает : соответствующий указатель.

g_static_private_set ()

void g_static_private_set (GStaticPrivate *private_key, gpointer data, GDestroyNotify notify);

Устанавливает ключевой указатель private_key для текущего потока и функцию notify вызываемую с этим указателем (NULL или не-NULL), каждый раз когда указатель установлен снова или каждый раз когда текущий поток завершается.

Эта функция работает даже если g_thread_init() не была вызвана. Если g_thread_init() вызвана позже, то data ключевые для private_key будут унаследованы только основным потоком, то есть тем который вызван g_thread_init().

Примечание

notify используется иначе чем destructor в g_private_new().

private_key : GStaticPrivate.
data : новый указатель.
notify : функция вызываемая с указателем каждый раз когда завершается текущий поток или указатель устанавливается снова.

g_static_private_free ()

void g_static_private_free (GStaticPrivate *private_key);

Освобождает все ресурсы распределённые для private_key.

Вы не должны вызывать эту функцию для GStaticPrivate с неограниченным жизненным циклом, то есть для объявленной статично 'static', но если GStaticPrivate член структуры и структура освобождается, вы должны также освободить GStaticPrivate.

private_key : освобождаемая GStaticPrivate.

GOnce

typedef struct { volatile GOnceStatus status; volatile gpointer retval; } GOnce;

Структура GOnce контролирует одноразовую функцию инициализации. Любые одноразовые функции инициализации должны иметь собственную уникальную структуру GOnce.

volatile GOnceStatus status; состояние GOnce
volatile gpointer retval; возвращаемое значение, если status равен G_ONCE_STATUS_READY

Начиная с версии 2.4


enum GOnceStatus

typedef enum { G_ONCE_STATUS_NOTCALLED, G_ONCE_STATUS_PROGRESS, G_ONCE_STATUS_READY } GOnceStatus;

Возвможные состояния одноразовых функций инициализации контролируемые структурой GOnce.

G_ONCE_STATUS_NOTCALLED функция не была вызвана.
G_ONCE_STATUS_PROGRESS вызов функции происходит в текущий момент.
G_ONCE_STATUS_READY функия вызвана.

Начиная с версии 2.4


G_ONCE_INIT

#define G_ONCE_INIT { G_ONCE_STATUS_NOTCALLED, NULL }

GOnce должна быть инициализирована перед использованием с помощью этого макроса.

GOnce my_once = G_ONCE_INIT;

Начиная с версии 2.4


g_once()

#define g_once(once, func, arg)

Первый вызов с полученной структурой GOnce struct вызывает функцию func с полученным аргументом. Последующие вызовы g_once() с той же структурой GOnce не вызывают func снова, а возвращают сохранённый результат первого вызова. Возвращаемое из g_once() состояние once будет G_ONCE_STATUS_READY.

Например, взаимоисключение или ключевые потоковые данные должны быть созданы только один раз. В потоковом окружении, вызов g_once() гарантирует что инициализация преобразована в последовательную форму через множество потоков.

Примечание

Рекурсивный вызов g_once() на той же самой структуре GOnce в func приведёт к зависанию.

gpointer get_debug_flags() { static GOnce my_once = G_ONCE_INIT; g_once (&my_once, parse_debug_flags, NULL); return my_once.retval; }

once : структура GOnce
func : GThreadFunc функция ассоциированная с once. Эта функция вызывается только один раз, не зависимо от количества раз помещения структуры GOnce в g_once() .
arg : данные помещаемые в func

Начиная с версии 2.4

Смотрите также

GThreadPool

Объединённые потоки.

GAsyncQueue

Отправка асинхронных сообщений между потоками.