Вначале хочу сказать, что теперь все выпуски можно найти в одном файле gtk_book.chm.
Использование
Glib(входит в состав GTK+) при написании многопоточных программ имеет свои
плюсы.
Самое очевидное – это переносимость, одно и то же приложение может быть совместимо с Unix, Windows и MacOS на уровне исходных кодов. К тому же Glib есть на всех Linux дистрибутивах, даже там, где нет GTK, да и для Windows приходится носить с собой гораздо меньше DLL библиотек, чем для всего GTK целиком. Если я Вас убедил использовать Glib для создания многопоточных программ, тогда читайте дальше.
printf("Не могу создать поток: %s", error->message);
return 0;
}
printf("сообщение из основной программы: %s\n",string);
// ждём окончания фонового потока
exit_cod = g_thread_join(thread_id);
printf("поток завершён с кодом: %d\n",exit_cod);
}
Мьютексы (Mutex)
Для синхронизации между потоками можно пользоваться мьютексами, которые позволяют выполнять последовательный доступ к совместным ресурсам из разных потоков.
Создание мьютекса:
GMutex* g_mutex_new();
Блокировка мьютекса:
void g_mutex_lock(GMutex *mutex);
Pазблокировка мьютекса:
void g_mutex_unlock(GMutex *mutex);
Удаление мьютекса:
void g_mutex_free(GMutex *mutex);
Пока мьютекс заблокирован, в других потоках функция g_mutex_lock() будет ждать разблокировки мьютекса, причём повторный вызов g_mutex_lock() в том же потоке будет проигнорирован, что защищает от зависаний приложения в некорректно написанных программах.
Для блокировки есть ещё одна функция: gboolean g_mutex_trylock(GMutex *mutex); которая пытается заблокировать мьютекс. Если мьютекс свободен, то он блокируется и функция возвращает TRUE, в противном случае функция сразу завершает
свою работу с кодом возврата FALSE.
Пример:
void func_critical (void)
{
static GMutex *mutex = NULL;
if (!mutex)
mutex = g_mutex_new();
g_mutex_lock(mutex);
// вычисления
//..
g_mutex_unlock(mutex);
}
Ещё есть статические мьютексы, которые создаются на этапе компиляции программы, а не во время выполнения(что может сэкономить немного времени при запуске программы); для них даже есть макросы:
int num=1;
G_LOCK_DEFINE (num); // определение номера мьютекса (выполняется один раз)
G_LOCK(num); // блокировка мьютекса
G_UNLOCK (num); // разблокировка мьютекса
Пример использования статического мьютекса:
G_LOCK_DEFINE(1);// для разнесения по времени проверки наличия файла и его удаления/создания
// проверка наличия файла
gboolean is_file(char *filename)
{
int result = FALSE;
G_LOCK(1);
result = g_file_test(filename,G_FILE_TEST_EXISTS);
G_UNLOCK(1)
return result;
}
// создание файла
FILE* create_file(char *filename)
{
FILE *file;
G_LOCK(1);
file = g_fopen(filename,"w");
G_UNLOCK(1)
return file;
}
Условия (Condition)
Условия также служат для синхронизации потоков, с их помощью можно устанавливать условия(события) и ждать их установки в другом потоке(бесконечно или не более заданного времени).
GCond* g_cond_new(void); - Создание условия(события)
void g_cond_wait (GCond *cond, GMutex *mutex); - ждать установки условия бесконечно.
void g_cond_timed_wait (GCond *cond, GMutex *mutex, GTimeVal *abs_time); - ждать установки условия не более заданного времени
Условия тесно связаны со мьютексами, установка или ожидание условия должны быть выполнены внутри мьютекса, причём функции ожидания g_cond_wait() или g_cond_timed_wait() временно разблокируют мьютекс внутри себя, позволяя зайти во мьютекс из другого потока для установки условия. Если что-то непонятно, то после того как взгляните на пример, всё должно стать ясно.
Пример использования: Рассмотрим случай запуска потока и ожидания его запуска с родительском потоке с помощью условий.
static GMutex *g_thread_start_mutex = NULL;// для g_thread_start_cond
static GCond *g_thread_start_cond = NULL;// для ожидания запуска потока
// Создание потока и ожидание его запуска с помощью условий
return_val = g_cond_timed_wait(g_thread_start_cond, g_thread_start_mutex,&timeval);// временно позволяет зайти в мьютекс g_thread_start_mutex
g_mutex_unlock(g_thread_start_mutex);
}
// потоковая функция;
gpointer my_thread(gpointer data)
{
// Установить g_thread_start_cond
g_mutex_lock (g_thread_start_mutex);
g_cond_signal(g_thread_start_cond);
g_mutex_unlock (g_thread_start_mutex);// сигнал установится только после покидания мьютекса
// выполнение чего-нибудь
// ...
return 0;
}
Есть ещё другие способы синхронизации потоков, которые Вы можете найти в документации на Glib (файл glib-Threads.html).
Единственное, на мой взгляд, чего не хватает, так это возможности принудительно прервать выполняемый поток из другого потока. Хотя, это может и правильно, грамотно написанная программа всегда будет иметь возможность штатным образом завершить поток.